<?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: Alex de Sousa</title>
    <description>The latest articles on DEV Community by Alex de Sousa (@alexdesousa).</description>
    <link>https://dev.to/alexdesousa</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%2F200308%2F518d2f4b-9a6b-454f-969f-b5ca81de9a8f.png</url>
      <title>DEV Community: Alex de Sousa</title>
      <link>https://dev.to/alexdesousa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexdesousa"/>
    <language>en</language>
    <item>
      <title>ICON 2.0: Realtime Decentralized Quote Prices</title>
      <dc:creator>Alex de Sousa</dc:creator>
      <pubDate>Tue, 08 Mar 2022 12:49:44 +0000</pubDate>
      <link>https://dev.to/alexdesousa/icon-20-realtime-decentralized-quote-prices-2990</link>
      <guid>https://dev.to/alexdesousa/icon-20-realtime-decentralized-quote-prices-2990</guid>
      <description>&lt;p&gt;&lt;a href="https://icon.foundation/"&gt;ICON 2.0&lt;/a&gt; is a smart contract platform with a unique interoperability proposition: &lt;a href="https://medium.com/helloiconworld/blockchain-transmission-protocol-btp-an-overview-744aaa51334e"&gt;Blockchain Transmission Protocol (BTP)&lt;/a&gt;. &lt;strong&gt;BTP&lt;/strong&gt; will enable &lt;strong&gt;decentralized and trustless&lt;/strong&gt; interoperability between &lt;strong&gt;ICON 2.0&lt;/strong&gt; and all blockchain that implement the protocol.&lt;/p&gt;

&lt;p&gt;At the time of publishing this article, the full &lt;strong&gt;BTP&lt;/strong&gt; is not yet released. However, some of its features are already available to everyone. One of these features is a websocket to receive realtime updates on either (or both) blocks produced and event logs emitted.&lt;/p&gt;

&lt;p&gt;This article will focus on how we can leverage this websocket connection to retrieve quote prices from &lt;a href="https://balanced.network"&gt;Balanced Decentralized Exchange&lt;/a&gt; by using the &lt;a href="https://github.com/alexdesousa/icon"&gt;Elixir ICON 2.0 SDK&lt;/a&gt; I wrote in the past few months.&lt;/p&gt;

&lt;p&gt;Without further ado, &lt;strong&gt;let's go!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/RrVzUOXldFe8M/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/RrVzUOXldFe8M/giphy.gif" alt="Let's go!" width="349" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;This article assumes you have downloaded &lt;code&gt;icon&lt;/code&gt; either within a new project using &lt;code&gt;mix new&lt;/code&gt; or using a script e.g:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;#/usr/bin/env elixir&lt;/span&gt;

&lt;span class="no"&gt;Mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:icon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.1"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# ... rest of the script ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also find the full script for getting &lt;em&gt;sICX/bnUSD&lt;/em&gt; quotes &lt;a href="https://gist.github.com/alexdesousa/e8cd1d64a62b74aed14f07b34ce811f3"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quote Prices
&lt;/h2&gt;

&lt;p&gt;When we want to exchange one token for another, we're presented with token pairs. These pairs have a &lt;em&gt;base token&lt;/em&gt;, a &lt;em&gt;quote token&lt;/em&gt; and, a &lt;em&gt;quote price&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D4jSDv2y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a05dd8xpdlzhx3exym4r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D4jSDv2y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a05dd8xpdlzhx3exym4r.png" alt="_ICX/USD = $0.60_: _ICX_ (ICON's token) priced in _USD_ is worth $0.60" width="880" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we, somehow, capture the event logs emitted every time someone exchanges one token for another, then we'll have realtime quote prices for any pair we want. In this article, we'll subscribe to &lt;strong&gt;Balanced&lt;/strong&gt; &lt;em&gt;sICX/bnUSD&lt;/em&gt; prices.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;sICX&lt;/em&gt; is staked ICX token and &lt;em&gt;bnUSD&lt;/em&gt; is an algorithmic stablecoin pegged to the dollar called Balanced Dollars.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Getting the Swap Log
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Balanced&lt;/strong&gt; is built from several SCOREs (Smart Contract On Reliable Environment) and one of these SCOREs handles all operations related to the decentralized exchange (DEx). The main DEx operation is swapping tokens e.g. we can use the contract to exchange our &lt;strong&gt;sICX&lt;/strong&gt; with &lt;strong&gt;bnUSD&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Furthermore, every time someone swaps a token, the SCORE will emit the following event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Swap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can query the SCORE's API with &lt;code&gt;Icon.get_score_api/2&lt;/code&gt; and get the meaning of each input. For that, we'll need the SCORE address, which is &lt;a href="https://tracker.icon.foundation/contract/cxa0af3165c08318e988cb30993b3048335b94af6c"&gt;cxa0af3165c08318e988cb30993b3048335b94af6c&lt;/a&gt; in the &lt;em&gt;Mainnet&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;dex_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cxa0af3165c08318e988cb30993b3048335b94af6c"&lt;/span&gt;
&lt;span class="n"&gt;identity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Icon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RPC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Identity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;network_id:&lt;/span&gt; &lt;span class="ss"&gt;:mainnet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Icon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_score_api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dex_score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;swap_definition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Swap"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"eventlog"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the variable &lt;code&gt;swap_definition&lt;/code&gt;, we'll have the &lt;code&gt;Swap&lt;/code&gt; event log definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="p"&gt;%{&lt;/span&gt;
  &lt;span class="s2"&gt;"inputs"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"indexed"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"0x1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"indexed"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"0x1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_baseToken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Address"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_fromToken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Address"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_toToken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Address"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_sender"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Address"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_receiver"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Address"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_fromValue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_toValue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_timestamp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_lpFees"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_balnFees"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_poolBase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_poolQuote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_endingPrice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"_effectiveFillPrice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Swap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"eventlog"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Analyzing the Swap Event
&lt;/h2&gt;

&lt;p&gt;With the previous definition, we can start making some assumptions about each event input and what things we need to get from them in order to have the price for &lt;em&gt;sICX/bnUSD&lt;/em&gt; every time the event is emitted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;_id&lt;/code&gt; is the token pair identifier (&lt;code&gt;indexed&lt;/code&gt; 1st position).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_baseToken&lt;/code&gt; is the SCORE address of the base token of the pair (&lt;code&gt;indexed&lt;/code&gt; 2nd position).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_fromToken&lt;/code&gt; is the SCORE address of the token being sold (&lt;code&gt;data&lt;/code&gt; 1st position).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_toToken&lt;/code&gt; is the SCORE address of the token being bought (&lt;code&gt;data&lt;/code&gt; 2nd position).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_timestamp&lt;/code&gt; is the UNIX epoch timestamp in UTC in microseconds (&lt;code&gt;data&lt;/code&gt; 7th position).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_endingPrice&lt;/code&gt; is the quote price after the swap in &lt;code&gt;loop&lt;/code&gt; (&lt;code&gt;data&lt;/code&gt; 12th position).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Either partially or fully, we can use the previous inputs to find the quote price we're looking for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/P1i1JsW2nNoBkn8xbb/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/P1i1JsW2nNoBkn8xbb/giphy.gif" alt="Detective" width="480" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Protip&lt;/strong&gt;: We can query the pool stats with the DEx function &lt;code&gt;getPoolStats&lt;/code&gt; and search by ID. This will give us &lt;em&gt;sICX/bnUSD&lt;/em&gt; pair has the ID 2. You can check it out yourself by running the following:&lt;/p&gt;


&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;Icon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dex_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"getPoolStats"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;_id:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="ss"&gt;call_schema:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;_id:&lt;/span&gt; &lt;span class="ss"&gt;:integer&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;p&gt;At the time of writing this article, there are 39 pools, but not all of them have a name, so in this case using &lt;code&gt;_baseToken&lt;/code&gt;, &lt;code&gt;_fromToken&lt;/code&gt;, and &lt;code&gt;_toToken&lt;/code&gt; addresses can help us figure out the actual pair name.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Realtime Updates
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/alexdesousa/icon"&gt;Elixir ICON 2.0 SDK&lt;/a&gt; has an &lt;a href="https://github.com/gmtprime/yggdrasil"&gt;Yggdrasil&lt;/a&gt; adapter for ICON's websocket. Thus we can use an &lt;em&gt;Yggdrasil&lt;/em&gt; process to subscribe to the &lt;code&gt;Swap&lt;/code&gt; event. In this case, the most important part is to define out channel correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Quotes&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;
  &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;Icon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;EventLog&lt;/span&gt;

  &lt;span class="nv"&gt;@dex_contract&lt;/span&gt; &lt;span class="s2"&gt;"cxa0af3165c08318e988cb30993b3048335b94af6c"&lt;/span&gt;
  &lt;span class="nv"&gt;@sicx_bnusd_id&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nv"&gt;@signature&lt;/span&gt; &lt;span class="s2"&gt;"Swap(int,Address,Address,Address,Address,Address,int,int,int,int,int,int,int,int,int)"&lt;/span&gt;

  &lt;span class="nv"&gt;@channel&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="ss"&gt;adapter:&lt;/span&gt; &lt;span class="ss"&gt;:icon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;
      &lt;span class="ss"&gt;source:&lt;/span&gt; &lt;span class="ss"&gt;:event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;data:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;
        &lt;span class="ss"&gt;addr:&lt;/span&gt; &lt;span class="nv"&gt;@dex_contract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;event:&lt;/span&gt; &lt;span class="nv"&gt;@signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;indexed:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;@sicx_bnusd_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="ss"&gt;from_height:&lt;/span&gt; &lt;span class="mi"&gt;47_077_000&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;@channel&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="c1"&gt;# ... handle_event/3 definition ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Protip&lt;/strong&gt;: I added &lt;code&gt;from_height: 47_077_000&lt;/code&gt; field to the channel's &lt;code&gt;name&lt;/code&gt; for subscribing to an older block height. This way, we don't need to wait for a &lt;code&gt;Swap&lt;/code&gt; event to happen and we can see some results right away. In general, this is only needed for testing purposes, but it wouldn't be needed if we just want the latest price.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Showing the Prices
&lt;/h2&gt;

&lt;p&gt;The events we'll receive will have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;_timestamp&lt;/code&gt; in microseconds in the 7th position of &lt;code&gt;data&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;_endingPrice&lt;/code&gt; in the 12th position of &lt;code&gt;data&lt;/code&gt; (&lt;code&gt;_endingPrice * 10¹⁸&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we can now define our &lt;code&gt;handle_event/3&lt;/code&gt; callback and finally print our quote price in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Quotes&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;
  &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;Icon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;EventLog&lt;/span&gt;

  &lt;span class="c1"&gt;# ... channel definition ...&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;EventLog&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;swap_event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;swap_event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;

    &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;timestamp&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_unix!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:microsecond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_iso8601&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1_000_000_000_000_000_000&lt;/span&gt;

    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] sICX/bnUSD price: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we execute this process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;Quotes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then we'll get the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[2022-03-07T16:17:15.095799Z] sICX/bnUSD price: 0.6762914917929946
[2022-03-07T16:33:31.390351Z] sICX/bnUSD price: 0.6765223727152583
[2022-03-07T16:19:25.140181Z] sICX/bnUSD price: 0.6763077935623969
[2022-03-07T16:06:10.885982Z] sICX/bnUSD price: 0.6696113966372721
[2022-03-07T16:28:17.210851Z] sICX/bnUSD price: 0.6765180152082558
[2022-03-07T16:27:41.288288Z] sICX/bnUSD price: 0.6764913565725723
... continues ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can check out the full script &lt;a href="https://gist.github.com/alexdesousa/e8cd1d64a62b74aed14f07b34ce811f3"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/it6W8D4FfvaPC/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/it6W8D4FfvaPC/giphy.gif" alt="Beautiful" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Though BTP is not fully released, we can already start leveraging some of its core features for our own advantage. Given ICON's block time is 3 seconds, this websocket connection gives us a tremendous advantage for building realtime bots for different purposes: from maximizing our yield to securing our favorite NFT latest drop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ICON 2.0 definitely&lt;/strong&gt; has a great advantage over many other &lt;em&gt;slow&lt;/em&gt; blockchains and I believe its DeFi ecosystem &lt;strong&gt;will have a bright future!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/jp8lWlBjGahPFAljBa/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/jp8lWlBjGahPFAljBa/giphy.gif" alt="The future is now" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover image by &lt;a href="https://unsplash.com/photos/fiXLQXAhCfk"&gt;Maxim Hopman&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>blockchain</category>
      <category>icon</category>
    </item>
    <item>
      <title>My Worst Bug</title>
      <dc:creator>Alex de Sousa</dc:creator>
      <pubDate>Wed, 22 Apr 2020 19:08:20 +0000</pubDate>
      <link>https://dev.to/alexdesousa/my-worst-bug-474k</link>
      <guid>https://dev.to/alexdesousa/my-worst-bug-474k</guid>
      <description>&lt;p&gt;Some bugs pull you to the abyss and leave you there. They're the Balrog to our Gandalf. Once you beat them though, you're wiser and more powerful!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/rBblrZDuvOaJi/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/rBblrZDuvOaJi/giphy.gif" alt="Gandalf falling!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was building a small plugin in C++ for an MT4 Server (ForEx trading server). The output of the project was a Windows DLL. Using the server's protocol, I managed to get JSON strings and parse them with &lt;a href="https://rapidjson.org/"&gt;RapidJSON&lt;/a&gt;. Everything ran smoothly in my virtual machine and in some development servers. Even Valgrind couldn't find memory leaks. I thought the plugin was ready for production...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Oh boy was I so wrong!&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: RapidJSON is an amazing library and if I need to parse JSON in C/C++ again, I would use it without hesitation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/EimNpKJpihLY4/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/EimNpKJpihLY4/giphy.gif" alt="Fail!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging The Problem
&lt;/h2&gt;

&lt;p&gt;Once I deployed the plugin, everything seemed fine... until the next day! A nasty segmentation fault killed the server. The plugin made us loose some money and I had to roll back the deploy.&lt;/p&gt;

&lt;p&gt;After several days of testing, I realized the production server always died with the same set of data. I was able to pinpoint the error to RapidJSON. Something weird was happening when the memory was allocated, but none of the tools I was using to debug this were reporting any problems.&lt;/p&gt;

&lt;p&gt;I was desperate, so I compiled the DLL with debug symbols and then I de-compiled it using &lt;a href="http://www.ollydbg.de/"&gt;OllyDBG&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I started reading the DLL assembly code ... &lt;strong&gt;for a week and a half&lt;/strong&gt;! Reading assembly was horrible. I considered switching careers. But then I got to the instruction that failed! Eureka! I couldn't believe it! It felt good to finally understand the bug!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/WR2W4OIee3YBQbIbID/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/WR2W4OIee3YBQbIbID/giphy.gif" alt="Eureka!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bug!
&lt;/h2&gt;

&lt;p&gt;The problem was that RapidJSON's custom allocator:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compressed the data in memory.&lt;/li&gt;
&lt;li&gt;Allocated only what it needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The production machine architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allocated the memory RapidJSON asked for.&lt;/li&gt;
&lt;li&gt;Ignored the way RapidJSON wanted the data to be structured.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's easier to see with an image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m6a_Qdys--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thebroken.link/assets/img/my-worst-bug/memory_allocation.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m6a_Qdys--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thebroken.link/assets/img/my-worst-bug/memory_allocation.png" alt="RapidJSON Custom Allocation Vs. What The Machine Actually Did"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If RapidJSON needed to store an integer, a string and a boolean value, then it could fail randomly depending on the length of the string. e.g. given the integer &lt;code&gt;42&lt;/code&gt; and the boolean &lt;code&gt;true&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For the string &lt;code&gt;Hey&lt;/code&gt;, id would succeed:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aU7mQoID--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thebroken.link/assets/img/my-worst-bug/memory_allocation_success.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aU7mQoID--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thebroken.link/assets/img/my-worst-bug/memory_allocation_success.png" alt="Memory allocation success"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For the string &lt;code&gt;Hi&lt;/code&gt;, it would fail:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N0wNoGKd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thebroken.link/assets/img/my-worst-bug/memory_allocation_fail.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N0wNoGKd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thebroken.link/assets/img/my-worst-bug/memory_allocation_fail.png" alt="Memory allocation fail"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A debugging nightmare!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;I just made RapidJSON use the machine's allocator instead of the custom one. Spent two weeks debugging something and changed just a single word in the code!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/10PDlC02A1L5Cw/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/10PDlC02A1L5Cw/giphy.gif" alt="I'm an idiot!"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Now I avoid C/C++ at all costs!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o7TKCEuECLAqDYEY8/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o7TKCEuECLAqDYEY8/giphy.gif" alt="Nightmare!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope whatever bug you're dealing with at the moment gets solved soon!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover photo by &lt;a href="https://unsplash.com/@jeztimms"&gt;Jez Timms&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>horror</category>
      <category>bug</category>
    </item>
    <item>
      <title>Yggdrasil as Distributed PubSub</title>
      <dc:creator>Alex de Sousa</dc:creator>
      <pubDate>Thu, 16 Apr 2020 16:23:24 +0000</pubDate>
      <link>https://dev.to/alexdesousa/yggdrasil-as-distributed-pubsub-3pj9</link>
      <guid>https://dev.to/alexdesousa/yggdrasil-as-distributed-pubsub-3pj9</guid>
      <description>&lt;p&gt;Yggdrasil's default adapter supports multi-node subscriptions out-of-the-box thanks to &lt;a href="https://github.com/phoenixframework/phoenix_pubsub"&gt;Phoenix PubSub&lt;/a&gt;. This distributed capabilities can be extended to any adapter compatible with Yggdrasil v5.0 without writing a single line of extra code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Qtpdtlk9rpb8PKK0ih/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Qtpdtlk9rpb8PKK0ih/giphy.gif" alt="Spock is interested!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start
&lt;/h2&gt;

&lt;p&gt;I've used &lt;a href="https://github.com/alexdesousa/alexdesousa.github.io/tree/blog/examples/matrix"&gt;this example project&lt;/a&gt; for the code in this article. You can skip this section safely as long as you remember the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Basic&lt;/code&gt; project has only Yggdrasil.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Rabbit&lt;/code&gt; project has Yggdrasil for RabbitMQ.&lt;/li&gt;
&lt;li&gt;A RabbitMQ server is available.&lt;/li&gt;
&lt;li&gt;The host name is called &lt;code&gt;matrix&lt;/code&gt;. Your machine's will be different.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to follow along with the examples in this article, you can download the example project using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--depth&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-b&lt;/span&gt; blog &lt;span class="se"&gt;\&lt;/span&gt;
  https://github.com/alexdesousa/alexdesousa.github.io.git examples &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;examples &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git filter-branch &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--prune-empty&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subdirectory-filter&lt;/span&gt; examples/matrix HEAD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the folder you'll find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/alexdesousa/alexdesousa.github.io/tree/blog/examples/matrix/basic"&gt;Basic project&lt;/a&gt; that has a basic version of &lt;a href="https://github.com/gmtprime/yggdrasil"&gt;Yggdrasil&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/alexdesousa/alexdesousa.github.io/tree/blog/examples/matrix/rabbit"&gt;Rabbit project&lt;/a&gt; that has &lt;a href="https://github.com/gmtprime/yggdrasil_rabbitmq"&gt;Yggdrasil for RabbitMQ&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A docker compose with a RabbitMQ server:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;rabbit &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker-compose up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://i.giphy.com/media/bKnEnd65zqxfq/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/bKnEnd65zqxfq/giphy.gif" alt="Make it so"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Message Distribution
&lt;/h2&gt;

&lt;p&gt;Yggdrasil's default adapter piggybacks on Phoenix PubSub for the message delivery, inheriting its distributed capabilities e.g. let's say we have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The node &lt;code&gt;:neo@matrix&lt;/code&gt; using &lt;code&gt;Basic&lt;/code&gt; project:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;$ &lt;/span&gt;iex &lt;span class="nt"&gt;--sname&lt;/span&gt; neo &lt;span class="nt"&gt;-S&lt;/span&gt; mix
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The node &lt;code&gt;:smith@matrix&lt;/code&gt; also using &lt;code&gt;Basic&lt;/code&gt; project:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;$ &lt;/span&gt;iex &lt;span class="nt"&gt;--sname&lt;/span&gt; smith &lt;span class="nt"&gt;-S&lt;/span&gt; mix
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Both nodes are interconnected:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;smith&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:neo&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;:smith@matrix&lt;/code&gt; can subscribe to any channel where &lt;code&gt;:neo@matrix&lt;/code&gt; is publishing messages e.g:&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;:smith@matrix&lt;/code&gt;, we subscribe to the channel &lt;code&gt;"private"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;smith&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;smith&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:Y_CONNECTED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Channel&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="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;:neo@matrix&lt;/code&gt;, we publish a message in the channel &lt;code&gt;"private"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;neo&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;neo&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"What's the Matrix?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, we can flush &lt;code&gt;:smith@matrix&lt;/code&gt; mailbox and find our message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;smith&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:Y_EVENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Channel&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="s2"&gt;"What's the Matrix?"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Distributed pubsub as simple as that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/dWy2WwcB3wvX8QA1Iu/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/dWy2WwcB3wvX8QA1Iu/giphy.gif" alt="Easy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bridged Message Distribution
&lt;/h2&gt;

&lt;p&gt;The bridge adapter makes a &lt;em&gt;bridge&lt;/em&gt; between any Yggdrasil adapter and the default adapter. This allows adapters to inherit the distributed capabilities of the default adapter e.g. let's say we have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The node &lt;code&gt;:neo@matrix&lt;/code&gt; using &lt;code&gt;Basic&lt;/code&gt; project:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;$ &lt;/span&gt;iex &lt;span class="nt"&gt;--sname&lt;/span&gt; neo &lt;span class="nt"&gt;-S&lt;/span&gt; mix
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The node &lt;code&gt;:trinity@matrix&lt;/code&gt; using &lt;code&gt;Rabbit&lt;/code&gt; project:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;$ &lt;/span&gt;iex &lt;span class="nt"&gt;--sname&lt;/span&gt; trinity &lt;span class="nt"&gt;-S&lt;/span&gt; mix
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The node &lt;code&gt;:trinity@matrix&lt;/code&gt; has access to a RabbitMQ server.&lt;/li&gt;
&lt;li&gt;Both nodes are interconnected:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trinity&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:neo&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So our final infrastructure would look like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E95MY6l_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thebroken.link/assets/img/yggdrasil-easy-pub-sub-in-elixir/bridge-adapter-example.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E95MY6l_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thebroken.link/assets/img/yggdrasil-easy-pub-sub-in-elixir/bridge-adapter-example.png" alt="Bridge adapter example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Through &lt;code&gt;:trinity@matrix&lt;/code&gt;, the node &lt;code&gt;:neo@matrix&lt;/code&gt; can now subscribe to&lt;br&gt;
a RabbitMQ exchange:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;neo&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"amq.topic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;adapter:&lt;/span&gt; &lt;span class="ss"&gt;:rabbitmq&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;neo&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;neo&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:Y_CONNECTED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Channel&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="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Or even publish messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;neo&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"What's the Matrix?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;neo&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:Y_EVENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Channel&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="s2"&gt;"What's the Matrix?"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The good thing about this feature is that it works with any adapter that supports Yggdrasil v5.0.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/1zRd5ZNo0s6kLPifL1/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/1zRd5ZNo0s6kLPifL1/giphy.gif" alt="The future is now!"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Yggdrasil bridge adapter allows you to convert any adapter in a distributed one by relying in Phoenix PubSub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/NHJ9pPZ9z1EHK/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/NHJ9pPZ9z1EHK/giphy.gif" alt="Bridge"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you found this useful and happy coding!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover photo by &lt;a href="https://unsplash.com/@quinoal"&gt;Quino Al&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>tutorial</category>
      <category>pubsub</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>My Road to 8 Week Streak</title>
      <dc:creator>Alex de Sousa</dc:creator>
      <pubDate>Fri, 10 Apr 2020 19:06:57 +0000</pubDate>
      <link>https://dev.to/alexdesousa/my-road-to-8-week-streak-54m7</link>
      <guid>https://dev.to/alexdesousa/my-road-to-8-week-streak-54m7</guid>
      <description>&lt;p&gt;9 weeks ago, I challenged myself to consistently write an article every week for at least 8 weeks. Today, I can say I did it. It was hard. Not the hardest thing I've done, but hard nonetheless.&lt;/p&gt;

&lt;p&gt;It wasn't just about writing something and get done with it for the week. The internet is already filled with shallow useless content. I didn't need to contribute to that. I wanted to write good content. Enjoyable and memorable content. Content that made me proud.&lt;/p&gt;

&lt;p&gt;Writing is hard. A blank page is intimidating. Hitting the &lt;em&gt;publish&lt;/em&gt; button and exposing your thoughts to the masses is scary. Quitting is easy, but unforgivable.&lt;/p&gt;

&lt;p&gt;Nonetheless, I wrote.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/R6xi8dXsRhIjK/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/R6xi8dXsRhIjK/giphy.gif" alt="Me writing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Good Content
&lt;/h2&gt;

&lt;p&gt;Beating the challenge wasn't the goal. The challenge kept my eyes on the goal. The goal was to promote my personal brand and my open source projects. I chose &lt;em&gt;content marketing&lt;/em&gt; as my tool to reach the goal. &lt;/p&gt;

&lt;p&gt;Everything I know about content marketing I learnt from &lt;a href="https://app.growthmentor.com/marta-olszewska"&gt;my girlfriend&lt;/a&gt;. I'm not a marketing expert, but rule number one of content marketing is: &lt;em&gt;write good content&lt;/em&gt;. The vaguest rule ever. So, what's good content?&lt;/p&gt;

&lt;h3&gt;
  
  
  Know Your Audience
&lt;/h3&gt;

&lt;p&gt;It's nice when someone shares your content. You reached them. Your content was valuable enough for them. They are your &lt;em&gt;target audience&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I love Elixir. I feel comfortable sharing my knowledge about it. Most of my projects are written in Elixir. My target audience was anybody interested in Elixir. So... I mostly wrote about Elixir.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3oD3YLLQBxZbBBYSWc/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3oD3YLLQBxZbBBYSWc/giphy.gif" alt="Your audience"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Shareable Content
&lt;/h3&gt;

&lt;p&gt;Writing for your target audience is not enough. Your content needs to be shareable. And the most important thing: it needs to be shareable by the right people.&lt;/p&gt;

&lt;p&gt;The right people are the experts in the area you chose. They have read a gazillion times about list comprehensions in Elixir. They haven't read about that time you implemented Prolog's backtracking using list comprehension in Elixir.&lt;/p&gt;

&lt;p&gt;Write about the cool stuff you have done. The things you're proud of.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/PudZiAbQDUEik/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/PudZiAbQDUEik/giphy.gif" alt="Miyagi nodding"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Enjoyable Content
&lt;/h3&gt;

&lt;p&gt;In the era of short attention span, you're worst enemy is sudden boredom. Use GIFs. Use images. Use graphics. Entertain while sharing knowledge.&lt;/p&gt;

&lt;p&gt;Your article's content is competing with a sudden WhatsApp message with a meme or that Prime Video notification saying The Expanse new season is available now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/SggILpMXO7Xt6/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/SggILpMXO7Xt6/giphy.gif" alt="Oh, look a dogo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Findable Content
&lt;/h3&gt;

&lt;p&gt;It doesn't matter you're the best technical writer in the world if nobody reads you. You need to distribute your content.&lt;/p&gt;

&lt;p&gt;This greatly depends on the area you chose. In my case, Elixir's community is small and getting into the newsletters is not that hard as long as your content is valuable.&lt;/p&gt;

&lt;p&gt;Don't wait for people to discover you. Reach out for them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Don't spam. Nobody likes spammers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o6ZsVObnL03crv4VG/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o6ZsVObnL03crv4VG/giphy.gif" alt="Paper boy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring Success
&lt;/h2&gt;

&lt;p&gt;Thinking you're writing good content is different than writing good content. I needed a way to measure my success. I tracked several metrics along the way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Number of DEV views.&lt;/li&gt;
&lt;li&gt;Number of DEV reactions.&lt;/li&gt;
&lt;li&gt;Number of DEV followers.&lt;/li&gt;
&lt;li&gt;Number of Github stars.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally to those quantitative metrics, I also took into consideration whether the articles were published in a newsletter or not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://elixirweekly.net/"&gt;Elixir Weekly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://plataformatec.com.br/elixir-radar/"&gt;Elixir Radar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I created a spreadsheet in Google Sheets and with &lt;a href="https://gist.github.com/alexdesousa/a23552ed2995a865895f53a4fd844c6b"&gt;this script&lt;/a&gt; I periodically gathered the data. This approach was inspired by/stolen from &lt;a href="https://dev.to/pluralsight/pulling-your-dev-to-stats-into-a-google-sheet-56dh"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8Dyv-xXC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thebroken.link/assets/img/my-road-to-8-week-streak/spreadsheet.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8Dyv-xXC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thebroken.link/assets/img/my-road-to-8-week-streak/spreadsheet.png" alt="Spreadsheet with my data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wasn't that rigorous with Twitter metrics, but I still kept them in mind when writing a tweet sharing my next article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Number of Twitter likes.&lt;/li&gt;
&lt;li&gt;Number of Twitter retweets.&lt;/li&gt;
&lt;li&gt;Number of Twitter followers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/6ZnDM7tOjKTRe/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/6ZnDM7tOjKTRe/giphy.gif" alt="Measuring"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;My articles were well received by the community. I got useful feedback that has improved both my projects and blog posts. I even got great pull requests to some of my projects and that makes me happy. People not only find my articles, but also my projects, useful!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/rdma0nDFZMR32/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/rdma0nDFZMR32/giphy.gif" alt="Happy pigglet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following is a performance summary for all blog posts and open source projects:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric (Number of)&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;Week 9&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DEV views&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;3319&lt;/td&gt;
&lt;td&gt;+3319&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEV reactions&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;125&lt;/td&gt;
&lt;td&gt;+125&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEV followers&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;164&lt;/td&gt;
&lt;td&gt;+164&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Github stars&lt;/td&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;td&gt;170&lt;/td&gt;
&lt;td&gt;+121&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Twitter followers&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;+27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Elixir Weekly publications&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;+4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Elixir Radar publications&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;+1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/RgfGmnVvt8Pfy/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/RgfGmnVvt8Pfy/giphy.gif" alt="That's good"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons
&lt;/h2&gt;

&lt;p&gt;Consistently writing good content is hard. The following are some of the lessons learnt these past 8 weeks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choose a publishing day&lt;/strong&gt;: My days were Thursdays. I needed to hit the
&lt;em&gt;publish&lt;/em&gt; button no matter what. Pressure is good.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick your article's topic one week before you publish it&lt;/strong&gt;: Some articles require experimentation and research. Good content requires time. Don't rush it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick a &lt;em&gt;backup topic&lt;/em&gt;&lt;/strong&gt;: If your main article is not ready for publishing, then publish a good article about the &lt;em&gt;backup topic&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write in &lt;em&gt;beast mode&lt;/em&gt;&lt;/strong&gt;: Write what's on your mind. Don't try to write a perfect article. Just write. Once your ideas are written, you can edit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distribute your content&lt;/strong&gt;: Once you publish the article, distribute it in every relevant channel you have at your disposal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/I5yH9Aeq0EGCA/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/I5yH9Aeq0EGCA/giphy.gif" alt="Climbing"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Writing is hard. Writing requires practice. The more you practice, the easier it gets. Just do it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/qDPg6HNz2NfAk/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/qDPg6HNz2NfAk/giphy.gif" alt="Just do it!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy writing!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover image by &lt;a href="https://unsplash.com/@aaronburden"&gt;Aaron Burden&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>career</category>
      <category>writing</category>
      <category>elixir</category>
    </item>
    <item>
      <title>AyeSQL: Writing Raw SQL</title>
      <dc:creator>Alex de Sousa</dc:creator>
      <pubDate>Thu, 02 Apr 2020 16:20:00 +0000</pubDate>
      <link>https://dev.to/alexdesousa/ayesql-writing-raw-sql-320k</link>
      <guid>https://dev.to/alexdesousa/ayesql-writing-raw-sql-320k</guid>
      <description>&lt;p&gt;Most developers consider that writing raw SQL (Structured Query Language) is a bad practice. The main arguments against it are that Object Relational Mappers (ORMs):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Abstract several SQL dialects.&lt;/li&gt;
&lt;li&gt;Have a flat learning curve unlike SQL.&lt;/li&gt;
&lt;li&gt;Optimize queries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ORMs allow you to abstract queries in an almost magical way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;# INSERT INTO user (name, age)
#   VALUES ('Bob', 18)
#   ON CONFLICT(name)
#   DO UPDATE SET name = EXCLUDED.name
#   RETURNING name, age
&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="cp"&gt;# UPDATE user
#   SET age = 17
#   WHERE name = 'Bob'
&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;17&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%2Fy0bz09q9sllvdc08iszf.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%2Fy0bz09q9sllvdc08iszf.gif" alt="Magic!"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Before we start
&lt;/h2&gt;

&lt;p&gt;The contents of this article are a mix between my work experience and the great insights &lt;a href="https://www.goodreads.com/book/show/36555832-mastering-postgresql-in-application-development" rel="noopener noreferrer"&gt;Mastering PostgreSQL in Application Development&lt;/a&gt; book provides.&lt;/p&gt;

&lt;p&gt;The code for this article can be found &lt;a href="https://github.com/alexdesousa/alexdesousa.github.io/tree/blog/examples/matrix" rel="noopener noreferrer"&gt;here&lt;/a&gt; and it can be downloaded with 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;git clone &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--depth&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-b&lt;/span&gt; blog &lt;span class="se"&gt;\&lt;/span&gt;
  https://github.com/alexdesousa/alexdesousa.github.io.git examples &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;examples &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git filter-branch &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--prune-empty&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subdirectory-filter&lt;/span&gt; examples/f1 HEAD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  "My ORM abstracts several SQL dialects"
&lt;/h2&gt;

&lt;p&gt;In my experience, I've found that it's more likely for an application to be re-written in another language e.g. Ruby to Elixir than actually migrating the data from one database to another e.g. MySQL to PostgreSQL.&lt;/p&gt;

&lt;p&gt;Additionally, running the database migrations in a different database is not a walk in the park either. Complex applications end up adding custom SQL to their migrations in order to optimize or automate certain processes: database extensions, indexes and triggers.&lt;/p&gt;

&lt;p&gt;And then, the data needs to be migrated e.g. &lt;code&gt;pgloader&lt;/code&gt; is an amazing tool that migrates data from MySQL, SQLite or MSSQL to PostgreSQL. However, sometimes these tools don't work correctly or they are nonexistent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/26ybwvTX4DTkwst6U/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/26ybwvTX4DTkwst6U/giphy.gif" alt="At least you tried"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  "My ORM has a flat learning curve unlike SQL"
&lt;/h2&gt;

&lt;p&gt;SQL is different. It's a functional language and most programmers are used to imperative languages. It's also hard to master. &lt;/p&gt;

&lt;p&gt;Ironically, the queries are almost in natural language e.g. if we show the following sentences to someone who doesn't know SQL, probably they'll know they have the same objective:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Get the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;age&lt;/code&gt; of every &lt;code&gt;client&lt;/code&gt; older than or equal to &lt;code&gt;18&lt;/code&gt;&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SELECT name, age FROM client WHERE age ≥ 18&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ORMs specifically work great for simple queries. However, when you need a more complex query, you end up doing certain workarounds to bypass the ORMs limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing raw SQL directly.&lt;/li&gt;
&lt;li&gt;Querying the database several times (sometimes unknowingly) to retrieve the data we need and doing the computation in our language instead of the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For now I'll focus on the second approach and I'll talk about the first one later. It's easier to see it with a small example:&lt;/p&gt;

&lt;p&gt;Given the following subset of tables from the &lt;a href="https://github.com/lerocha/chinook-database" rel="noopener noreferrer"&gt;Chinook database&lt;/a&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%2Fthebroken.link%2Fassets%2Fimg%2Fayesql-writing-raw-sql-in-elixir%2Fmusic.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%2Fthebroken.link%2Fassets%2Fimg%2Fayesql-writing-raw-sql-in-elixir%2Fmusic.png" alt="Chinook database"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want to know the &lt;em&gt;duration of every album Pearl Jam has ever made&lt;/em&gt; (or at least the ones listed in the database). In an OO language we would do something like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Artist&lt;/span&gt; &lt;span class="n"&gt;artist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Artist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Pearl Jam"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;album&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Album&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;artistId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;albumId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;album&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;milliseconds&lt;/span&gt;

  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"#{album.title} | #{format_time(ms)}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For a developer who doesn't know SQL, this would seem like reasonable code. In reality, it's highly inefficient:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;We're requesting one artist by name:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"Artist"&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"Name"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Pearl Jam'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then requesting all albums by artist id:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"Album"&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"ArtistId"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For every album, we're requesting the tracks by album id:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"Track"&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"AlbumId"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;album&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This database contains 5 Pearl Jam albums, so we would be querying the database 7 times. The magic behind the ORM is deceiving us. In every round trip to the database, we need to consider two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Time the query takes to execute.&lt;/li&gt;
&lt;li&gt;Time the network transmits the data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good ORMs introduce ways of dealing with this type of problem. However, knowing we need to use those tools in order to have good performance in our queries requires knowing SQL.&lt;/p&gt;

&lt;p&gt;So, going back to the title of this section: Do ORMs really have a flat learning curve? Considering programmers need to know SQL to understand the tools ORMs offer and SQL is hard, then I guess they have not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/1oJLpejP9jEvWQlZj4/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/1oJLpejP9jEvWQlZj4/giphy.gif" alt="Visible confusion"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The previous problem can be solved with one query:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Chinook database has capitalized table and column names. That's why they need to be between double quotes.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="nv"&gt;"Album"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"Title"&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;album&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"Track"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"Milliseconds"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'1 ms'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"Album"&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="nv"&gt;"Artist"&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"ArtistId"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="nv"&gt;"Track"&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"AlbumId"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;"Artist"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"Name"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Pearl Jam'&lt;/span&gt;
  &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;album&lt;/span&gt;
  &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;album&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and our result would be already formatted:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          album          |   duration  
-------------------------+-------------
 Live On Two Legs [Live] | 01:11:18.954
 Pearl Jam               | 00:49:43.857
 Riot Act                | 00:54:16.468
 Ten                     | 00:53:25.871
 Vs.                     | 00:46:17.674
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  "My ORM optimizes queries"
&lt;/h2&gt;

&lt;p&gt;Good ORMs do optimize queries. Usually they optimize queries for the most common cases. However, if the problem we're solving is not covered by these optimizations, it can be a real head-scratcher.&lt;/p&gt;

&lt;p&gt;In the end, we end up with a subpar solution or writing raw SQL to overcome the limitations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/D1ETZoAPSt5EA/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/D1ETZoAPSt5EA/giphy.gif" alt="Duct taping broken wall"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Language Mappers
&lt;/h2&gt;

&lt;p&gt;There are alternatives to ORMs. In Elixir, we have &lt;a href="https://github.com/elixir-ecto/ecto" rel="noopener noreferrer"&gt;Ecto&lt;/a&gt;. It's not an ORM, but a language mapper. It gives you a Domain Specific Language (DSL) for dealing with SQL queries in Elixir e.g. again if we want to know the &lt;em&gt;duration of every album Pearl Jam has ever made&lt;/em&gt; (or the 5 listed in this database), we would do the following:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;al&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="no"&gt;Album&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;inner_join:&lt;/span&gt; &lt;span class="n"&gt;ar&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="no"&gt;Artist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on:&lt;/span&gt; &lt;span class="n"&gt;al&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;artistId&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;artistId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;left_join:&lt;/span&gt; &lt;span class="n"&gt;tr&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="no"&gt;Track&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on:&lt;/span&gt; &lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;albumId&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;albumId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;where:&lt;/span&gt; &lt;span class="n"&gt;ar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Pearl Jam"&lt;/span&gt;
    &lt;span class="ss"&gt;group_by:&lt;/span&gt; &lt;span class="n"&gt;al&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;
    &lt;span class="ss"&gt;order_by:&lt;/span&gt; &lt;span class="n"&gt;al&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;
    &lt;span class="ss"&gt;select:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;album:&lt;/span&gt; &lt;span class="n"&gt;al&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;duration:&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;milliseconds&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Language mappers require developers to know SQL, but allows them to &lt;em&gt;almost&lt;/em&gt; never leave the comfort of their language. That &lt;em&gt;"almost"&lt;/em&gt; is important.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/RgfGmnVvt8Pfy/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/RgfGmnVvt8Pfy/giphy.gif" alt="That's good"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Though not everything is sunshine and rainbows. Language mappers try to abstract several SQL dialects in an unified DSL. This means that not all the SQL of our database is going to be included in this DSL.&lt;/p&gt;

&lt;p&gt;To overcome this limitation, Ecto introduces the concept of &lt;em&gt;fragments&lt;/em&gt;. Fragments are pieces of custom SQL code that can be added to our queries e.g. given the following subset from &lt;a href="https://ergast.com/mrd/db/" rel="noopener noreferrer"&gt;Ergast Developer API&lt;/a&gt; database:&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%2Fthebroken.link%2Fassets%2Fimg%2Fayesql-writing-raw-sql-in-elixir%2Ff1.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%2Fthebroken.link%2Fassets%2Fimg%2Fayesql-writing-raw-sql-in-elixir%2Ff1.png" alt="Ergast Developer API database"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's say we want to get the percentage of accidents per participant in F1 seasons between the years 1974 and 1990. In SQL, we would have the following:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;accidents&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;year&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;races&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;season&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="k"&gt;COUNT&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="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="k"&gt;COUNT&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="n"&gt;FILTER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Accident'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;accidents&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;f1db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;
      &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;f1db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statusid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;f1db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;races&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raceid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;season&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;season&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;accidents&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;participants&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="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;percentage&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;accidents&lt;/span&gt;
   &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;season&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="mi"&gt;1974&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="mi"&gt;1990&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;season&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, when translating this query to Ecto, we'll have to use fragments:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# NOTE: The schema definition is omitted.&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1974&lt;/span&gt;
&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1990&lt;/span&gt;

&lt;span class="n"&gt;accidents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="no"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;join:&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;assoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="ss"&gt;join:&lt;/span&gt; &lt;span class="n"&gt;ra&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;assoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:race&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="ss"&gt;group_by:&lt;/span&gt; &lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"season"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="ss"&gt;select:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;
      &lt;span class="ss"&gt;season:&lt;/span&gt; &lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"EXTRACT(year from ?)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="ss"&gt;participants:&lt;/span&gt; &lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"COUNT(*)"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="ss"&gt;accidents:&lt;/span&gt; &lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"COUNT(*) FILTER(WHERE status = 'Accident')"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;subquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accidents&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="ss"&gt;where:&lt;/span&gt; &lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"? BETWEEN ? AND ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;season&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="ss"&gt;select:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;
      &lt;span class="ss"&gt;season:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;season&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;percentage:&lt;/span&gt; &lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ROUND(100.0 * ? / ?, 2)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accidents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="no"&gt;F1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The code above is hard to read, hard to write and error prone. Yet we don't have any performance improvements in our query. A maintenance nightmare.&lt;/p&gt;

&lt;p&gt;We could re-write the previous Ecto query differently by encapsulating them in Elixir macros and then importing our custom DSL e.g:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CustomDSL&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Query&lt;/span&gt;

  &lt;span class="k"&gt;defmacro&lt;/span&gt; &lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="kn"&gt;quote&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"EXTRACT(year from ?)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kn"&gt;unquote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;But again, we wouldn't get any improvements performance-wise. Just readability... in Ecto. It was perfectly readable in SQL.&lt;/p&gt;

&lt;p&gt;The consequences are clear. Developers need knowledge of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The specific SQL dialect (in this case PostgreSQL dialect).&lt;/li&gt;
&lt;li&gt;Ecto's API and its limitations.&lt;/li&gt;
&lt;li&gt;Elixir's macros for fragment encapsulation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, now they need to maintain a new custom DSL API with its documentation. If we only have a few of this complex queries in our project, is it worthy?&lt;/p&gt;

&lt;p&gt;What's worse, after the refactor, we could end up with a subpar solution or wasting our time entirely.&lt;/p&gt;
&lt;h2&gt;
  
  
  Raw SQL
&lt;/h2&gt;

&lt;p&gt;So far we've seen ORMs and language mappers are good for general problems we might encounter. However, some other problems are better left in raw SQL.&lt;/p&gt;

&lt;p&gt;ORMs, language mappers and database adapters usually provide an API for running raw SQL. In Elixir, Ecto and &lt;a href="https://github.com/elixir-ecto/postgrex" rel="noopener noreferrer"&gt;Postgrex&lt;/a&gt; give us the function &lt;code&gt;query/2&lt;/code&gt; e.g. in Ecto we would do the following:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="sd"&gt;"""
  WITH accidents AS
  (
      SELECT EXTRACT(year from races.date) AS season,
             COUNT(*) AS participants,
             COUNT(*) FILTER(WHERE status = 'Accident') AS accidents
        FROM results
        JOIN status USING(statusid)
        JOIN races USING(raceid)
    GROUP BY season
  )
    SELECT season,
           ROUND(100.0 * accidents / participants, 2) AS percentage
      FROM accidents
     WHERE season BETWEEN $1 AND $2
  ORDER BY season
  """&lt;/span&gt;

&lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Adapters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;F1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1974&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1990&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;There are some things I'd like to point out from the previous code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$1&lt;/code&gt; and &lt;code&gt;$2&lt;/code&gt; are the query parameters: The numbers indicate the position in the parameter list. If we add status as a variable instead of the constant &lt;code&gt;'Accidents'&lt;/code&gt;, we would need to update the other indexes.&lt;/li&gt;
&lt;li&gt;The query is a string: usually editors wouldn't highlight the SQL syntax inside the string.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;columns&lt;/code&gt; and &lt;code&gt;rows&lt;/code&gt; are separated in the result:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Postgrex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;columns:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"season"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"percentage"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="ss"&gt;command:&lt;/span&gt; &lt;span class="ss"&gt;:select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;connection_id:&lt;/span&gt; &lt;span class="mi"&gt;206&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;messages:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
      &lt;span class="ss"&gt;num_rows:&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;rows:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1974.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;#Decimal&amp;lt;3.67&amp;gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1975.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;#Decimal&amp;lt;14.88&amp;gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1976.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;#Decimal&amp;lt;11.06&amp;gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1977.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;#Decimal&amp;lt;12.58&amp;gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1978.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;#Decimal&amp;lt;10.19&amp;gt;],&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;The complexity it's still there.&lt;/p&gt;
&lt;h2&gt;
  
  
  Meet AyeSQL
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/alexdesousa" rel="noopener noreferrer"&gt;
        alexdesousa
      &lt;/a&gt; / &lt;a href="https://github.com/alexdesousa/ayesql" rel="noopener noreferrer"&gt;
        ayesql
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Library for using raw SQL in Elixir
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Inspired by Clojure library &lt;a href="https://github.com/krisajenkins/yesql" rel="noopener noreferrer"&gt;Yesql&lt;/a&gt;, AyeSQL tries to find a middle ground between raw SQL strings and SQL language mappers by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeping SQL in SQL files.&lt;/li&gt;
&lt;li&gt;Generating Elixir functions for every query.&lt;/li&gt;
&lt;li&gt;Working out of the box with PostgreSQL using Ecto or Postgrex.&lt;/li&gt;
&lt;li&gt;Being extendable to support other databases and outputs.&lt;/li&gt;
&lt;li&gt;Allowing some query composition.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Defining the same query in AyeSQL, we would need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A file for defining our query in raw SQL:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="ss"&gt;file:&lt;/span&gt; &lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;f1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;season&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;

   &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="n"&gt;get_accidents&lt;/span&gt;
   &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="ss"&gt;docs:&lt;/span&gt; &lt;span class="no"&gt;Gets&lt;/span&gt; &lt;span class="n"&gt;accidents&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;season&lt;/span&gt; &lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
   &lt;span class="no"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;accidents&lt;/span&gt; &lt;span class="no"&gt;AS&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="no"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;races&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;season&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="no"&gt;COUNT&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="no"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="no"&gt;COUNT&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="no"&gt;FILTER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Accident'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;accidents&lt;/span&gt;
         &lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;f1db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="no"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;
         &lt;span class="no"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;f1db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="no"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="no"&gt;USING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statusid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="no"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;f1db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;races&lt;/span&gt; &lt;span class="no"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;races&lt;/span&gt; &lt;span class="no"&gt;USING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raceid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="no"&gt;GROUP&lt;/span&gt; &lt;span class="no"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;season&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;season&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="no"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;accidents&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;participants&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="no"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;percentage&lt;/span&gt;
       &lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;accidents&lt;/span&gt;
      &lt;span class="no"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;season&lt;/span&gt; &lt;span class="no"&gt;BETWEEN&lt;/span&gt; &lt;span class="ss"&gt;:from&lt;/span&gt; &lt;span class="no"&gt;AND&lt;/span&gt; &lt;span class="ss"&gt;:to&lt;/span&gt;
   &lt;span class="no"&gt;ORDER&lt;/span&gt; &lt;span class="no"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;season&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A file for declaring our queries as Elixir functions:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="c1"&gt;# file: lib/f1/queries.ex&lt;/span&gt;

   &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;AyeSQL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;defqueries:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

   &lt;span class="n"&gt;defqueries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;F1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Season&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"query/season.sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;repo:&lt;/span&gt; &lt;span class="no"&gt;F1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Modify our configuration to run the queries by default:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="c1"&gt;# file: config/config.exs&lt;/span&gt;

   &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Config&lt;/span&gt;

   &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:ayesql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;run?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can call our query as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;F1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Season&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_accidents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;from:&lt;/span&gt; &lt;span class="mi"&gt;1974&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to:&lt;/span&gt; &lt;span class="mi"&gt;1990&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;percentage:&lt;/span&gt; &lt;span class="c1"&gt;#Decimal&amp;lt;3.67&amp;gt;, season: 1974.0},&lt;/span&gt;
   &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;percentage:&lt;/span&gt; &lt;span class="c1"&gt;#Decimal&amp;lt;14.88&amp;gt;, season: 1975.0},&lt;/span&gt;
   &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;percentage:&lt;/span&gt; &lt;span class="c1"&gt;#Decimal&amp;lt;11.06&amp;gt;, season: 1976.0},&lt;/span&gt;
   &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;percentage:&lt;/span&gt; &lt;span class="c1"&gt;#Decimal&amp;lt;12.58&amp;gt;, season: 1977.0},&lt;/span&gt;
   &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;percentage:&lt;/span&gt; &lt;span class="c1"&gt;#Decimal&amp;lt;10.19&amp;gt;, season: 1978.0},&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;I don't know about you, but IMHO this seems like a maintainable way of dealing with complex SQL queries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/cdGQHR4Qzefx6/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/cdGQHR4Qzefx6/giphy.gif" alt="easy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AyeSQL provides query runners for Ecto and Postgrex out-of-the-box. In the previous example we used Ecto's runner. If we need a different output or query a different database e.g. MSSQL, we could implement the behaviour &lt;code&gt;AyeSQL.Runner&lt;/code&gt; for our use case.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In the past year, my team and I had to write an Elixir application with a pre-existent MSSQL database. &lt;code&gt;AyeSQL.Runner&lt;/code&gt; behaviour and &lt;a href="https://github.com/livehelpnow/tds" rel="noopener noreferrer"&gt;TDS&lt;/a&gt; library allowed us to query the database with ease.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;I'm hoping this article helps change the negative reputation raw SQL queries have.&lt;/p&gt;

&lt;p&gt;Yesql inspired libraries like AyeSQL, can help you to be more productive when your ORM or language mapper have failed you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/fdhZs6PUqxSQU/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/fdhZs6PUqxSQU/giphy.gif" alt="There is another"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'll write more about AyeSQL features in future articles, but for now happy coding!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover image by &lt;a href="https://unsplash.com/@arstyy" rel="noopener noreferrer"&gt;Austin Neill&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>sql</category>
      <category>postgres</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Elixir Pubsub In Less Than 50 Lines</title>
      <dc:creator>Alex de Sousa</dc:creator>
      <pubDate>Thu, 26 Mar 2020 20:50:35 +0000</pubDate>
      <link>https://dev.to/alexdesousa/elixir-pubsub-in-less-than-50-lines-1hal</link>
      <guid>https://dev.to/alexdesousa/elixir-pubsub-in-less-than-50-lines-1hal</guid>
      <description>&lt;p&gt;&lt;code&gt;:pg2&lt;/code&gt; is a mostly unknown, but powerful Erlang module. It provides an API for creating process groups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Process Group
&lt;/h2&gt;

&lt;p&gt;So, what's a process group? Well... it's a group of Erlang/Elixir processes.&lt;/p&gt;

&lt;p&gt;Perhaps, the correct question would be, why do we care about process groups? Well, process groups are the foundation for publisher-subscribers (pubsubs for short).&lt;/p&gt;

&lt;h2&gt;
  
  
  PG2
&lt;/h2&gt;

&lt;p&gt;Understanding &lt;code&gt;:pg2&lt;/code&gt; API and how it relates to a pubsub API will make it easier to understand: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every process group is a &lt;code&gt;channel&lt;/code&gt; e.g. a group called &lt;code&gt;:my_channel&lt;/code&gt; is created:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:my_channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Every process in a group is a &lt;code&gt;subscriber&lt;/code&gt; e.g. &lt;code&gt;self()&lt;/code&gt; is part of &lt;code&gt;:my_channel&lt;/code&gt; group:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:my_channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
   &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;publisher&lt;/code&gt; can &lt;code&gt;send/2&lt;/code&gt; messages to a &lt;code&gt;channel&lt;/code&gt; e.g. the publisher gets all the members of the group &lt;code&gt;:my_channel&lt;/code&gt; and sends &lt;code&gt;"Some message"&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;members&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_members&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:my_channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="ss"&gt;:ok&lt;/span&gt;
   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;member&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;members&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;member&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Some message"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;subscriber&lt;/code&gt; will receive the messages in its mailbox:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="s2"&gt;"Some message"&lt;/span&gt;
   &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;subscriber&lt;/code&gt; can unsubscribe from a &lt;code&gt;channel&lt;/code&gt; e.g. &lt;code&gt;self()&lt;/code&gt; leaves the group &lt;code&gt;:my_channel&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:my_channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
   &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;channel&lt;/code&gt; can be deleted:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:my_channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And that's it! That's the API. And you know what's the best thing about it? &lt;strong&gt;It can work between connected nodes&lt;/strong&gt;. Keep reading and you'll see :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/mcueTtCHvqNPy/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/mcueTtCHvqNPy/giphy.gif" alt="Message in cereal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing a PubSub
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;PubSub&lt;/code&gt; has three main functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;subscribe/1&lt;/code&gt; for subscribing to a &lt;code&gt;channel&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

     &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_members&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;members&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;members&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;members&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
           &lt;span class="ss"&gt;:ok&lt;/span&gt;                     &lt;span class="c1"&gt;# It's already subscribed.&lt;/span&gt;
         &lt;span class="k"&gt;else&lt;/span&gt;
           &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Subscribes to channel&lt;/span&gt;
         &lt;span class="k"&gt;end&lt;/span&gt;

       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:no_such_group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
         &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# Creates channel&lt;/span&gt;
         &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# Subscribe to channel&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;ul&gt;
&lt;li&gt;
&lt;code&gt;unsubscribe/1&lt;/code&gt; for unsubscribing from a &lt;code&gt;channel&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_members&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# Unsubscribes from channel&lt;/span&gt;
          &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&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;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# Deletes the channel&lt;/span&gt;

        &lt;span class="n"&gt;members&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;members&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;members&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Unsubscribes from channel&lt;/span&gt;
          &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="ss"&gt;:ok&lt;/span&gt;                      &lt;span class="c1"&gt;# It's already unsubscribed&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="ss"&gt;:ok&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;ul&gt;
&lt;li&gt;
&lt;code&gt;publish/2&lt;/code&gt; for sending a &lt;code&gt;message&lt;/code&gt; to a &lt;code&gt;channel&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="ss"&gt;:pg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_members&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;members&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
         &lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;member&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;members&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;member&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="ss"&gt;:ok&lt;/span&gt;

       &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
         &lt;span class="ss"&gt;:ok&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;For a full implementation of &lt;code&gt;PubSub&lt;/code&gt; you can check &lt;a href="https://gist.github.com/alexdesousa/4d592fe206cca17393affaefa4c8fd33"&gt;this gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I usually create a &lt;code&gt;.iex.exs&lt;/code&gt; file in my &lt;code&gt;$HOME&lt;/code&gt; folder and then run &lt;code&gt;iex&lt;/code&gt;. You could do the same with the previous gist by doing the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;~ &lt;span class="nv"&gt;$ PUBSUB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://gist.githubusercontent.com/alexdesousa/4d592fe206cca17393affaefa4c8fd33/raw/4d84894f016bd9eef84bba647c77c62b9c9a6094/pub_sub.ex"&lt;/span&gt;
~ &lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PUBSUB&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; .iex.exs
~ &lt;span class="nv"&gt;$ &lt;/span&gt;iex
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o7btNa0RUYa5E7iiQ/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o7btNa0RUYa5E7iiQ/giphy.gif" alt="It's that easy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Distributed PubSub
&lt;/h2&gt;

&lt;p&gt;For our distributed experiment we'll need two nodes. My machine is called &lt;code&gt;matrix&lt;/code&gt; and both nodes will be &lt;code&gt;neo&lt;/code&gt; and &lt;code&gt;trinity&lt;/code&gt; respectively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:neo@matrix&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;   alex@matrix ~ &lt;span class="nv"&gt;$ &lt;/span&gt;iex &lt;span class="nt"&gt;--sname&lt;/span&gt; neo
   iex&lt;span class="o"&gt;(&lt;/span&gt;neo@matrix&lt;span class="o"&gt;)&lt;/span&gt;1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:trinity@matrix&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;   alex@matrix ~ &lt;span class="nv"&gt;$ &lt;/span&gt;iex &lt;span class="nt"&gt;--sname&lt;/span&gt; trinity
   iex&lt;span class="o"&gt;(&lt;/span&gt;trinity@matrix&lt;span class="o"&gt;)&lt;/span&gt;1&amp;gt; Node.connect&lt;span class="o"&gt;(&lt;/span&gt;:neo@matrix&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Connects both nodes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;:neo@matrix&lt;/code&gt; can subscribe to &lt;code&gt;:mainframe&lt;/code&gt; channel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;neo&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;PubSub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:mainframe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And &lt;code&gt;:trinity@matrix&lt;/code&gt; can send a message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trinity&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;PubSub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:mainframe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Wake up, Neo..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Sometimes it takes a bit of time for nodes to synchronize their process groups, so you might need to &lt;code&gt;publish/2&lt;/code&gt; your message twice.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, &lt;code&gt;:neo@matrix&lt;/code&gt; should receive the message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;neo&lt;/span&gt;&lt;span class="nv"&gt;@matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="s2"&gt;"Wake up, Neo..."&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And that's it. A powerful pubsub in a few lines of code thanks to &lt;code&gt;:pg2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/S27iRp6ypEcnK/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/S27iRp6ypEcnK/giphy.gif" alt="Follow the white rabbit"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Erlang has several built-in hidden gems like &lt;code&gt;:pg2&lt;/code&gt; that make our lives easier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/PdfNwG98g6Sxq/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/PdfNwG98g6Sxq/giphy.gif" alt="gem"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover image by &lt;a href="https://unsplash.com/@artnok"&gt;Nicolas Picard&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>erlang</category>
      <category>pubsub</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Managing Dotfiles with Ansible</title>
      <dc:creator>Alex de Sousa</dc:creator>
      <pubDate>Thu, 19 Mar 2020 17:32:05 +0000</pubDate>
      <link>https://dev.to/alexdesousa/managing-dotfiles-with-ansible-3kbg</link>
      <guid>https://dev.to/alexdesousa/managing-dotfiles-with-ansible-3kbg</guid>
      <description>&lt;p&gt;When developers get tired of configuring again and again our machines, we tend to create a dotfiles repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Dotfiles Repository
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Dotfiles&lt;/strong&gt; are commonly used for storing user preferences or preserving the state of a utility, and are frequently created implicitly by using various utilities.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It all starts with a small repository that contains configuration files for common tools e.g. &lt;code&gt;.zshrc&lt;/code&gt;, &lt;code&gt;.vimrc&lt;/code&gt;, etc. However, every time we configure a new machine, we need to copy those files &lt;em&gt;by hand&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We pride ourselves with our ability to automate any task. Therefore, the next logical step for a dotfiles repository is to create a "small" shell script for automating tool installation and machine configuration.&lt;/p&gt;

&lt;p&gt;All is well at first, but that "small" script ends up being hundreds of lines long and hard to maintain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/S0KRynVEROiOs/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/S0KRynVEROiOs/giphy.gif" alt="This is madness"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ansible to the Rescue
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.ansible.com/"&gt;Ansible&lt;/a&gt; is an configuration management tool. It provides its own language to describe system configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is not a new idea, but it feels like one: configure your own machine the same way you configure your servers... With Ansible.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I'm using Debian throughout this tutorial, though the same can be accomplished with any architecture and operative system as long as Ansible is available for it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/F1YaFvtJ7VlwA/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/F1YaFvtJ7VlwA/giphy.gif" alt="Where's Ansible's supersuit?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Bootstrapping Ansible
&lt;/h3&gt;

&lt;p&gt;For installing and configuring stuff with Ansible, we need to first install Ansible. We can use a small shell script to accomplish this. Some of the variables like &lt;code&gt;$HOSTS&lt;/code&gt; and &lt;code&gt;$PLAYBOOK&lt;/code&gt; will make sense in the next sections:&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;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Dotfiles' project root directory&lt;/span&gt;
&lt;span class="nv"&gt;ROOTDIR&lt;/span&gt;&lt;span class="o"&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;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASH_SOURCE&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c"&gt;# Host file location&lt;/span&gt;
&lt;span class="nv"&gt;HOSTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROOTDIR&lt;/span&gt;&lt;span class="s2"&gt;/hosts"&lt;/span&gt;
&lt;span class="c"&gt;# Main playbook&lt;/span&gt;
&lt;span class="nv"&gt;PLAYBOOK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROOTDIR&lt;/span&gt;&lt;span class="s2"&gt;/dotfiles.yml"&lt;/span&gt;

&lt;span class="c"&gt;# Installs ansible&lt;/span&gt;
apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ansible

&lt;span class="c"&gt;# Runs Ansible playbook using our user.&lt;/span&gt;
ansible-playbook &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOSTS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PLAYBOOK&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--ask-become-pass&lt;/span&gt;

&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the previous script as our sudoer user will effectively install Ansible and run our main &lt;em&gt;playbook&lt;/em&gt; with all our &lt;em&gt;roles&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Playbook&lt;/strong&gt;: A file that defines several tasks to be executed in a target machine.&lt;br&gt;
&lt;strong&gt;Role&lt;/strong&gt;: Organizes multiple, related tasks with the data needed to run those tasks (variables, files, templates).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Basic Folder Structure
&lt;/h3&gt;

&lt;p&gt;Once we have our bootstrap script in place, we can start writing Ansible configuration files.&lt;/p&gt;

&lt;p&gt;In our dotfiles repository, our target is our own machine. This is very easy to define in our &lt;code&gt;hosts&lt;/code&gt; file (with local connection so it doesn't require an ssh key):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# file: hosts&lt;/span&gt;
&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;local&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="s"&gt;localhost&lt;/span&gt;

&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="s"&gt;ansible_connection=local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In our &lt;code&gt;bootstrap.sh&lt;/code&gt; script, this file is found in the variable &lt;code&gt;$HOSTS&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then we need a &lt;em&gt;playbook&lt;/em&gt; that lists every &lt;em&gt;role&lt;/em&gt; we want to deploy and configure in our machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# file: dotfiles.yml&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up local workstation&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
  &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zsh&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;zsh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our example, we'll install &lt;code&gt;zsh&lt;/code&gt; role.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In our &lt;code&gt;bootstrap.sh&lt;/code&gt; script, this files is found in the variable &lt;code&gt;$PLAYBOOK&lt;/code&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In general, our folder structure would look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;└ dotfiles
  - bootstrap.sh
  - dotfiles.yml
  - hosts
  └ roles
    └ zsh
      └ files
        - zshrc.link
      └ tasks
        - main.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ZSh role
&lt;/h3&gt;

&lt;p&gt;The following would be our &lt;code&gt;zsh&lt;/code&gt; role tasks for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing &lt;code&gt;zsh&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Setting it up as our default shell.&lt;/li&gt;
&lt;li&gt;Installing Oh-My-ZSH.&lt;/li&gt;
&lt;li&gt;Linking our &lt;code&gt;.zshrc&lt;/code&gt; configuration file to our home folder.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;--------&lt;/span&gt;
&lt;span class="c1"&gt;# file: roles/zsh/tasks/main.yml&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Installs zsh | apt&lt;/span&gt;
  &lt;span class="na"&gt;become&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;become_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
  &lt;span class="na"&gt;apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zsh&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;present&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Installs curl | apt&lt;/span&gt;
  &lt;span class="na"&gt;become&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;become_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
  &lt;span class="na"&gt;apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;curl&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;present&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sets zsh as default shell for my user | command&lt;/span&gt;
  &lt;span class="na"&gt;become&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;become_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
  &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chsh -s /bin/zsh {{ lookup('env' 'USER') }}&lt;/span&gt;
  &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zsh_for_user&lt;/span&gt;
  &lt;span class="na"&gt;failed_when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zsh_for_user.rc &amp;gt;= &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;changed_when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zsh_for_user.rc == &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checks for oh-my-zsh installation | stat&lt;/span&gt;
  &lt;span class="na"&gt;stat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lookup('env',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'HOME')&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/.oh-my-zsh"&lt;/span&gt;
  &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oh_my_zsh_stat&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Installs oh-my-zsh | raw&lt;/span&gt;
  &lt;span class="na"&gt;raw&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sh&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-c&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"$(curl&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-fsSL&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"'&lt;/span&gt;
  &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;not oh_my_zsh_stat.stat.exists&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Links .zshrc file | file&lt;/span&gt;
  &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lookup('env',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'ROOTDIR')&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/roles/zsh/files/zshrc.link"&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lookup('env',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'HOME')&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/.zshrc"&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;link&lt;/span&gt;
    &lt;span class="na"&gt;force&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running Our Bootstrap Script
&lt;/h3&gt;

&lt;p&gt;Finally, we can run our script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/dotfiles &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x bootstrap.sh
~/dotfiles &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;bootstrap.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install Ansible.&lt;/li&gt;
&lt;li&gt;Run our playbook, which means installing and configuring &lt;code&gt;zsh&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o84sq21TxDH6PyYms/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o84sq21TxDH6PyYms/giphy.gif" alt="Unlimited power"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;There are more cool features you can use to customize your system using Ansible. You can check my &lt;a href="https://github.com/alexdesousa/dotfiles"&gt;dotfiles&lt;/a&gt; repository if you want to see a fully working example.&lt;/p&gt;

&lt;p&gt;Additionally, if you want to know more about Ansible, you can check &lt;a href="https://serversforhackers.com/c/an-ansible2-tutorial"&gt;this amazing tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope you find this useful :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/kdiLau77NE9Z8vxGSO/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/kdiLau77NE9Z8vxGSO/giphy.gif" alt="Installation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover image by &lt;a href="https://unsplash.com/@joshriemer"&gt;Josh Riemer&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>dotfiles</category>
    </item>
    <item>
      <title>Oath: Don't Loose Your Keys!</title>
      <dc:creator>Alex de Sousa</dc:creator>
      <pubDate>Thu, 12 Mar 2020 22:25:32 +0000</pubDate>
      <link>https://dev.to/alexdesousa/oath-don-t-loose-your-keys-90</link>
      <guid>https://dev.to/alexdesousa/oath-don-t-loose-your-keys-90</guid>
      <description>&lt;p&gt;Recently, I needed to reset my phone. I spent some time backing up my pictures and documents. Everything was going great. But then, I hit a roadblock.&lt;/p&gt;

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

&lt;p&gt;One-time passwords have become very handy for logging into several sites from Twitter to Coinbase. This passwords are 6 digit tokens generated using the current time and a private key. I was using Google Authenticator for getting my one-time passwords. Sadly, this app does not provide a way to backup the private keys.&lt;/p&gt;

&lt;p&gt;The damage was done. I couldn't retrieve the private keys, so I needed to regenerate all of them in every site individually. I thought about my future self dealing with the same issue and I knew I needed a sustainable solution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/llbEoVMhkLngWlzVVa/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/llbEoVMhkLngWlzVVa/giphy.gif" alt="Let's change the lightbulb"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The research
&lt;/h2&gt;

&lt;p&gt;I wanted a one-time password solution that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Didn't rely on my phone or any app.&lt;/li&gt;
&lt;li&gt;Could also be used in my computer.&lt;/li&gt;
&lt;li&gt;Was offline (no private keys stored in the cloud).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's when I discovered &lt;code&gt;oathtool&lt;/code&gt;: a command line tool for generating 6 digit tokens given a private key.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I installed it using &lt;code&gt;sudo apt install oathtool&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Generating a 6 digit token with &lt;code&gt;oathtool&lt;/code&gt; is as easy as doing the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;oathtool &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nt"&gt;--totp&lt;/span&gt; &lt;span class="s1"&gt;'MyPrivateKey'&lt;/span&gt;
798946
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Discovering this tool was a good start, but I needed a good way of dealing with the private keys. Then I stumbled upon &lt;a href="https://www.cyberciti.biz/faq/use-oathtool-linux-command-line-for-2-step-verification-2fa/"&gt;this article&lt;/a&gt;. The author created two scripts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One for encrypting the private key into a file using &lt;code&gt;gpg2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;One for decrypting the private key and retrieving the 6 digit token using &lt;code&gt;oathtool&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, the 6 digit token was automatically copied to the clipboard using &lt;code&gt;xclip&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I installed both tools by running &lt;code&gt;sudo apt install gnupg2 xclip&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I loved the solution! Though it had some flaws like storing temporarily an unencrypted file with the private key, it was a great idea :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Mjq9vmDuJlBKw/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Mjq9vmDuJlBKw/giphy.gif" alt="Great idea"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The plugin
&lt;/h2&gt;

&lt;p&gt;I wrote &lt;a href="https://github.com/alexdesousa/oath"&gt;Oath ZSH plugin&lt;/a&gt; by gathering the best ideas from that article. I ended up with the following commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding a private key:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;$ &lt;/span&gt;oath add twitter.com
   Private key:
   &lt;span class="o"&gt;[&lt;/span&gt;SUCCESS]  Key created &lt;span class="k"&gt;for &lt;/span&gt;twitter.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Showing a 6 digit token (it'll ask for the gpg password):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;$ &lt;/span&gt;oath twitter.com
   123456
   &lt;span class="o"&gt;[&lt;/span&gt;SUCCESS]  Code copied to clipboard
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Deleting a private key (it'll ask for the gpg password):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;$ &lt;/span&gt;oath delete twitter.com
   &lt;span class="o"&gt;[&lt;/span&gt;WARN]     Deleting /home/user/.oath/twitter.com/B743BC73B5F90E2305142D226BBCD02E89ABBC79.gpg.gpg
   &lt;span class="o"&gt;[&lt;/span&gt;WARN]     Deleting /home/user/.oath/twitter.com
   &lt;span class="o"&gt;[&lt;/span&gt;SUCCESS]  Key deleted &lt;span class="k"&gt;for &lt;/span&gt;twitter.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The same private keys I added to &lt;code&gt;oath&lt;/code&gt;, I also added them to my phone's Google Authenticator app. That way both, my computer and phone, generate the same 6 digit token at a given time.&lt;/p&gt;

&lt;p&gt;The only difference is that now I can backup everything. I just need to copy the following folders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$HOME/.gnupg/&lt;/code&gt;: GPG folder with all the gpg keys.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$HOME/.oath/&lt;/code&gt;: Oath folder where all the private keys are stored.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;For more info, visit &lt;a href="https://github.com/alexdesousa/oath"&gt;Oath Github repository&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Xd1EhywNhOjq5EPn5t/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Xd1EhywNhOjq5EPn5t/giphy.gif" alt="Safety"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Though this solution might not be for everyone, it solves the problem I had. Now I can reset my phone at any time and not worrying about my private keys, because they're safely backed up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/de0AlLgV7XTRhEudoL/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/de0AlLgV7XTRhEudoL/giphy.gif" alt="The keys"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover image by &lt;a href="https://unsplash.com/@chunlea"&gt;Chunlea Ju&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>security</category>
      <category>zsh</category>
      <category>plugin</category>
    </item>
    <item>
      <title>Yggdrasil and RabbitMQ Subscriptions</title>
      <dc:creator>Alex de Sousa</dc:creator>
      <pubDate>Thu, 05 Mar 2020 21:39:47 +0000</pubDate>
      <link>https://dev.to/alexdesousa/yggdrasil-and-rabbitmq-subscriptions-18k4</link>
      <guid>https://dev.to/alexdesousa/yggdrasil-and-rabbitmq-subscriptions-18k4</guid>
      <description>&lt;p&gt;One of the features I really like about RabbitMQ is its queue routing. Its flexibility allows you to do interesting things without much of a hassle. But before I dig deep into RabbitMQ's routing capabilities, I would like to mention some concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connections and Channels
&lt;/h3&gt;

&lt;p&gt;RabbitMQ uses not only &lt;strong&gt;connections&lt;/strong&gt;, but virtual connections called &lt;strong&gt;channels&lt;/strong&gt;. The idea of channels is to introduce multiplexing in a single connection. A small system could establish only one connection with RabbitMQ while opening a channel for every single execution thread e.g:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AW3azsvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://thebroken.link/assets/img/yggdrasil-easy-pub-sub-in-elixir/rabbitmq-connection.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AW3azsvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://thebroken.link/assets/img/yggdrasil-easy-pub-sub-in-elixir/rabbitmq-connection.png" alt="RabbitMQ connection multiplexing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The rule of thumb would be to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One connection per application.&lt;/li&gt;
&lt;li&gt;One channel per process in the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Once our connection starts to be overloaded, we can start adding more connections to our connection pool.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With a normal RabbitMQ setup, we need to deal with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connection pools&lt;/strong&gt;: avoiding over consuming resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Channel cleaning&lt;/strong&gt;: avoiding channel memory leaks when they are not closed properly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fault-tolerant connections&lt;/strong&gt;: supporting re-connections in case of failure or disconnection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-connection back-off time&lt;/strong&gt;: avoiding overloading the database on multiple re-connections.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Exchanges and Queues
&lt;/h3&gt;

&lt;p&gt;An &lt;strong&gt;exchange&lt;/strong&gt; is a message router. Every &lt;strong&gt;queue&lt;/strong&gt; attached to it will be identified by a &lt;strong&gt;routing key&lt;/strong&gt;. Typically, routing keys are words separated by dots e.g. &lt;code&gt;spain.barcelona.gracia&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, routing keys support wildcards, for example: &lt;code&gt;spain.barcelona.*&lt;/code&gt; will match messages with routing keys like &lt;code&gt;spain.barcelona.gracia&lt;/code&gt; and &lt;code&gt;spain.barcelona.raval&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's easier to see these concepts with an image example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gu9Hke7H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://thebroken.link/assets/img/yggdrasil-easy-pub-sub-in-elixir/rabbitmq-exchange.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gu9Hke7H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://thebroken.link/assets/img/yggdrasil-easy-pub-sub-in-elixir/rabbitmq-exchange.png" alt="RabbitMQ message routing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the previous image:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;strong&gt;Publisher X&lt;/strong&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;strong&gt;Publisher Y&lt;/strong&gt;&lt;/strong&gt; are sending messages to &lt;strong&gt;&lt;em&gt;Exchange logs&lt;/em&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;strong&gt;Subscriber A&lt;/strong&gt;&lt;/strong&gt; is subscribed to &lt;code&gt;logs.*&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;strong&gt;Subscriber B&lt;/strong&gt;&lt;/strong&gt; is subscribed to &lt;code&gt;logs.error&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;strong&gt;Publisher X&lt;/strong&gt;&lt;/strong&gt; message will end up in &lt;strong&gt;&lt;em&gt;Queue&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;logs.info&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;strong&gt;Publisher Y&lt;/strong&gt;&lt;/strong&gt; message will end up in &lt;strong&gt;&lt;em&gt;Queue&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;logs.error&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;strong&gt;Subscriber A&lt;/strong&gt;&lt;/strong&gt; will receive &lt;strong&gt;&lt;strong&gt;Publisher X&lt;/strong&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;strong&gt;Publisher Y&lt;/strong&gt;&lt;/strong&gt;'s messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;strong&gt;Subscriber B&lt;/strong&gt;&lt;/strong&gt; will receive &lt;strong&gt;&lt;strong&gt;Publisher Y&lt;/strong&gt;&lt;/strong&gt;'s message.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o6gDSdED1B5wjC2Gc/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o6gDSdED1B5wjC2Gc/giphy.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Subscriptions in Yggdrasil
&lt;/h3&gt;

&lt;p&gt;Handling RabbitMQ's complexity might be intimidating. Fortunately, &lt;a href="https://github.com/gmtprime/yggdrasil_rabbitmq"&gt;Yggdrasil for RabbitMQ&lt;/a&gt; generalizes the complexity in order to have a simpler API.&lt;/p&gt;

&lt;p&gt;The biggest difference with previous adapters is the channel name. Instead of being a string, it's a tuple with the exchange name and the routing key e.g:&lt;/p&gt;

&lt;p&gt;A subscriber would connect to the exchange &lt;code&gt;amq.topic&lt;/code&gt; using the routing key &lt;code&gt;logs.*&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"amq.topic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"logs.*"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;adapter:&lt;/span&gt; &lt;span class="ss"&gt;:rabbitmq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:Y_CONNECTED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Channel&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The exchange must exist and its type should be &lt;code&gt;topic&lt;/code&gt;. The exchange &lt;code&gt;amq.topic&lt;/code&gt; is created by default in RabbitMQ.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then a publisher could send a message to the exchange &lt;code&gt;amq.topic&lt;/code&gt; using &lt;code&gt;logs.info&lt;/code&gt; as routing key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;publisher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"amq.topic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"logs.info"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;adapter:&lt;/span&gt; &lt;span class="ss"&gt;:rabbitmq&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;"Some message"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the subscriber would receive the message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:Y_EVENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Channel&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="s2"&gt;"Some message"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, the subscriber can be written using the &lt;code&gt;Yggdrasil&lt;/code&gt; behaviour:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Subscriber&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;\\&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"amq.topic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"logs.*"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="ss"&gt;adapter:&lt;/span&gt; &lt;span class="ss"&gt;:rabbitmq&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lost Messages
&lt;/h3&gt;

&lt;p&gt;Yggdrasil will acknowledge the messages as soon as they arrive to the adapter, then it will broadcast them to all the subscribers. If the adapter is alive while the subscribers are restarting/failing, some messages might be lost.&lt;/p&gt;

&lt;p&gt;Though it's possible to overcome this problem with exclusive queues, this feature is not implemented yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/gmtprime/yggdrasil_rabbitmq"&gt;Yggdrasil for RabbitMQ&lt;/a&gt; handles RabbitMQ complexity let's you focus in what really matters: &lt;strong&gt;messages&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover image by &lt;a href="https://unsplash.com/@abnair"&gt;Aswathy N&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>pubsub</category>
      <category>rabbitmq</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Yggdrasil and PostgreSQL Notifications</title>
      <dc:creator>Alex de Sousa</dc:creator>
      <pubDate>Wed, 26 Feb 2020 16:29:01 +0000</pubDate>
      <link>https://dev.to/alexdesousa/yggdrasil-and-postgresql-notifications-31kb</link>
      <guid>https://dev.to/alexdesousa/yggdrasil-and-postgresql-notifications-31kb</guid>
      <description>&lt;p&gt;One thing I really like about PostgreSQL is its notifications via &lt;code&gt;pg_notify&lt;/code&gt;. This feature is very useful when trying to get real-time notifications for certain changes in a databases.&lt;/p&gt;

&lt;h3&gt;
  
  
  PostgreSQL notifications
&lt;/h3&gt;

&lt;p&gt;Creating notifications in PostgreSQL is very easy e.g. let's say we have a table for books:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- User table creation&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and we want JSON notifications in the channel &lt;code&gt;new_books&lt;/code&gt; every time a new book is created in our database e.g:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Animal Farm"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trigger could be implemented as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Trigger function creation&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;trigger_new_book&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
  &lt;span class="k"&gt;DECLARE&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;BEGIN&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;PERFORM&lt;/span&gt; &lt;span class="n"&gt;pg_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'new_books'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Sets the trigger function in 'books' table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;books_notify_new_book&lt;/span&gt;
  &lt;span class="k"&gt;BEFORE&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt;
  &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;EACH&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;
  &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="k"&gt;PROCEDURE&lt;/span&gt; &lt;span class="n"&gt;trigger_new_book&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, the following query would trigger our JSON message in the channel &lt;code&gt;new_books&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Animal Farm'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Though subscribing to our database notifications can be done easily with &lt;a href="https://github.com/elixir-ecto/postgrex"&gt;Postgrex&lt;/a&gt; library, handling the connections to the database is a bit of a hassle. We need to ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connection multiplexing&lt;/strong&gt;: avoiding over consuming database resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fault-tolerant connections&lt;/strong&gt;: supporting re-connections in case of failure or disconnection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-connection back-off time&lt;/strong&gt;: avoiding overloading the database on multiple re-connections.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/FrLKYbLI0djKU/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/FrLKYbLI0djKU/giphy.gif" alt="problem"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/gmtprime/yggdrasil_postgres"&gt;Yggdrasil for PostgreSQL&lt;/a&gt; is an adapter that supports all the features mentioned above while maintaining Yggdrasil's simple API e.g:&lt;/p&gt;

&lt;p&gt;For our example, we could subscribe to the database messages by doing the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"new_books"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;adapter:&lt;/span&gt; &lt;span class="ss"&gt;:postgres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;transformer:&lt;/span&gt; &lt;span class="ss"&gt;:json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:Y_CONNECTED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Channel&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1984'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will get the following message in IEx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:Y_EVENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Channel&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="s2"&gt;"id"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"title"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"1984"&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;&lt;strong&gt;Note&lt;/strong&gt;: &lt;code&gt;Yggdrasil&lt;/code&gt; comes with built-in message transformers. We've used&lt;br&gt;
&lt;code&gt;:json&lt;/code&gt; transformer for this example in order to get a map from the JSON&lt;br&gt;
data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Additionally, our subscriber could also be an &lt;code&gt;Yggdrasil&lt;/code&gt; process e.g:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Books&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Subscriber&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;\\&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"new_books"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;adapter:&lt;/span&gt; &lt;span class="ss"&gt;:postgres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;transformer:&lt;/span&gt; &lt;span class="ss"&gt;:json&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"title"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's also possbible to use &lt;code&gt;Yggdrasil.publish/2&lt;/code&gt; with PostgreSQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"title"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"A Brave New World"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"new_books"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;adapter:&lt;/span&gt; &lt;span class="ss"&gt;:postgres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;transformer:&lt;/span&gt; &lt;span class="ss"&gt;:json&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://i.giphy.com/media/zcCGBRQshGdt6/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/zcCGBRQshGdt6/giphy.gif" alt="Too easy!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/gmtprime/yggdrasil_postgres"&gt;Yggdrasil for PostgreSQL&lt;/a&gt; simplifies subscriptions to PostgreSQL notifications and let's you focus in what really matters: &lt;strong&gt;messages&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/6k5aYgBKTxe3C/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/6k5aYgBKTxe3C/giphy.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover image by &lt;a href="https://unsplash.com/@nasa"&gt;Nasa&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>pubsub</category>
      <category>postgres</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Yggdrasil: Easy Pub-Sub in Elixir</title>
      <dc:creator>Alex de Sousa</dc:creator>
      <pubDate>Thu, 20 Feb 2020 08:58:17 +0000</pubDate>
      <link>https://dev.to/alexdesousa/yggdrasil-easy-pub-sub-in-elixir-3909</link>
      <guid>https://dev.to/alexdesousa/yggdrasil-easy-pub-sub-in-elixir-3909</guid>
      <description>&lt;p&gt;When I started coding in Elixir (around 2016), I was working for a financial company. Our product automatically invested money in the Forex market by copying traders' actions (&lt;em&gt;market orders&lt;/em&gt;) in real time. We had the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dk3D1XGD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thebroken.link/assets/img/yggdrasil-easy-pub-sub-in-elixir/system.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dk3D1XGD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thebroken.link/assets/img/yggdrasil-easy-pub-sub-in-elixir/system.png" alt="Our system"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In words, our system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Subscribed&lt;/strong&gt; to PostgreSQL for receiving &lt;em&gt;trader actions&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Published&lt;/strong&gt; to RabbitMQ for:

&lt;ul&gt;
&lt;li&gt;Categorizing &lt;em&gt;trader actions&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;And enqueuing &lt;em&gt;trader actions&lt;/em&gt; in the proper queue.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscribed&lt;/strong&gt; to Redis for receiving updates on prices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscribed&lt;/strong&gt; to several RabbitMQ queues for:

&lt;ul&gt;
&lt;li&gt;Receiving the categorized &lt;em&gt;trader actions&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;And deciding whether it should open/close some &lt;em&gt;market orders&lt;/em&gt; or not.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Opened and closed &lt;em&gt;market orders&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We needed to be able to communicate with three systems (PostgreSQL, RabbitMQ and Redis). However, in general, we only needed three actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;subscribe/1&lt;/code&gt; to a channel.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;publish/2&lt;/code&gt; a message in a channel.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unsubscribe/1&lt;/code&gt; from a channel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we could generalize those three actions into an API, we could then implement three individual adapters for every system to handle the annoying stuff like disconnections, failures, resource management, protocols, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/aih5IZkussTiE/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/aih5IZkussTiE/giphy.gif" alt="hard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet Yggdrasil
&lt;/h2&gt;

&lt;p&gt;Handling subscriptions should be easy and, in an ideal world, we would only need to know &lt;em&gt;where&lt;/em&gt; to connect and &lt;em&gt;start receiving&lt;/em&gt; messages right away.&lt;/p&gt;

&lt;p&gt;We shouldn't need to worry about secondary (yet relevant) things like disconnections, failures and managing resources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ij26UTQU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/gmtprime/yggdrasil/master/priv/static/yggdrasil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ij26UTQU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/gmtprime/yggdrasil/master/priv/static/yggdrasil.png" alt="Yggdrasil"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Yggdrasil&lt;/em&gt; is an immense mythical tree that connects the nine worlds in Norse cosmology.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/gmtprime/yggdrasil"&gt;Yggdrasil&lt;/a&gt; was our pub-sub generalization. Using the strong foundations of Phoenix pub-sub library, we built an agnostic publisher/subscriber application that has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi node support.&lt;/li&gt;
&lt;li&gt;Simple API: &lt;code&gt;subscribe/1&lt;/code&gt;, &lt;code&gt;unsubscribe/1&lt;/code&gt; and &lt;code&gt;publish/2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;GenServer&lt;/code&gt; wrapper for handling subscriber events easily.&lt;/li&gt;
&lt;li&gt;A basic adapter for using Elixir message distribution.&lt;/li&gt;
&lt;li&gt;Fault-tolerant adapters for:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/gmtprime/yggdrasil_redis"&gt;Redis&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/gmtprime/yggdrasil_postgres"&gt;PostgreSQL&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/gmtprime/yggdrasil_rabbitmq"&gt;RabbitMQ&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  One API to rule them all
&lt;/h2&gt;

&lt;p&gt;Yggdrasil's API is very simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A process subscribes to &lt;code&gt;"my_channel"&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"my_channel"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:Y_CONNECTED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Channel&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A process (in this case the same process) publishes the message &lt;code&gt;"my message"&lt;/code&gt; in &lt;code&gt;"my_channel"&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"my_channel"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;"my message"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The message should be in the mailbox of the subscriber process:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:Y_EVENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Channel&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="s2"&gt;"my message"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The subscriber can unsubscribe from the channel to stop receiving messages:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"my_channel"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:Y_DISCONNECTED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Channel&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;flush()&lt;/code&gt; cleans the IEx process mailbox. In general, receiving Yggdrasil messages should be the same as receiving messages when the sender uses &lt;code&gt;send/2&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/cdGQHR4Qzefx6/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/cdGQHR4Qzefx6/giphy.gif" alt="So easy!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Yggdrasil behaviour
&lt;/h3&gt;

&lt;p&gt;Yggdrasil provides a &lt;code&gt;behaviour&lt;/code&gt; for writing subscribers easily. Following the previous example, the subscriber could be written as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Subscriber&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start_link&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"my_channel"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:mailbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This subscriber will print the message as it receives it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_pid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Subscriber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Yggdrasil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"my_channel"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;"my message"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:mailbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"my_message"&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;An interesting side-effect is that now we can send messages to any process as long as they are subscribed to the right channel without needing to know the process PID or name.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;&lt;a href="https://github.com/gmtprime/yggdrasil"&gt;Yggdrasil&lt;/a&gt; hides the complexity of a pub/sub and let's you focus in what really matters: &lt;strong&gt;messages&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Hope you found this article useful. Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/26xBENWdka2DSvvag/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/26xBENWdka2DSvvag/giphy.gif" alt="Heck yeah!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover image by &lt;a href="https://unsplash.com/@toddquackenbush"&gt;Todd Quackenbush&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>pubsub</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Skogsrå: Simplifying Your Elixir Configuration</title>
      <dc:creator>Alex de Sousa</dc:creator>
      <pubDate>Thu, 13 Feb 2020 10:14:22 +0000</pubDate>
      <link>https://dev.to/alexdesousa/skogsra-simplifying-your-elixir-configuration-35im</link>
      <guid>https://dev.to/alexdesousa/skogsra-simplifying-your-elixir-configuration-35im</guid>
      <description>&lt;p&gt;Once an Elixir project is large enough, maintaining config files and configuration variables becomes a nightmare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuration variables are scattered throughout the code so it's very easy to forget a configuration setting.&lt;/li&gt;
&lt;li&gt;OS environment variables must be casted to the correct type as they are always strings.&lt;/li&gt;
&lt;li&gt;Required variables must be checked by hand.&lt;/li&gt;
&lt;li&gt;Setting defaults can sometimes be a bit cumbersome.&lt;/li&gt;
&lt;li&gt;No type safety.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/S0KRynVEROiOs/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/S0KRynVEROiOs/giphy.gif" alt="madness"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ideally though, configurations should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documented.&lt;/li&gt;
&lt;li&gt;Easy to find.&lt;/li&gt;
&lt;li&gt;Easy to read.&lt;/li&gt;
&lt;li&gt;Declarative.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In summary: &lt;strong&gt;easy to maintain&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;We'll elaborate using the the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:myapp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;hostname:&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"HOSTNAME"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;port:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"PORT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;"80"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The previous code is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Undocumented: &lt;code&gt;hostname&lt;/code&gt; and &lt;code&gt;port&lt;/code&gt; of what?&lt;/li&gt;
&lt;li&gt;Hard to read: Too many concerns in a single line.&lt;/li&gt;
&lt;li&gt;Hard to find: where are these &lt;code&gt;hostname&lt;/code&gt; and &lt;code&gt;port&lt;/code&gt; used?&lt;/li&gt;
&lt;li&gt;Not declarative: we're telling Elixir &lt;strong&gt;&lt;em&gt;how&lt;/em&gt; to retrieve the values&lt;/strong&gt; instead of &lt;strong&gt;&lt;em&gt;what&lt;/em&gt; are the values we want&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conclusion: &lt;strong&gt;it's hard to maintain&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing a config module
&lt;/h2&gt;

&lt;p&gt;We could mitigate some of these problems with one simple approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a module for your configs.&lt;/li&gt;
&lt;li&gt;Create a function for every single configuration parameter you app has.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following, though a bit more verbose, would be the equivalent to the previous config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Myapp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="s2"&gt;"My app config."&lt;/span&gt;

  &lt;span class="nv"&gt;@doc&lt;/span&gt; &lt;span class="s2"&gt;"My hostname"&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"HOSTNAME"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;"localhost"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nv"&gt;@doc&lt;/span&gt; &lt;span class="s2"&gt;"My port"&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"PORT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;"80"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Unlike our original code, this one is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documented: Every function has &lt;code&gt;@doc&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;Easy to find: We just need to to look for calls to functions defined in this module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, we still have essentially the same code we had before, which is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hard to read.&lt;/li&gt;
&lt;li&gt;Not declarative.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's gotta be a better way!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/WTdg5GBR45X6NbxqJK/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/WTdg5GBR45X6NbxqJK/giphy.gif" alt="almost"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  There is a better way - Meet Skogsrå
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/gmtprime/skogsra"&gt;Skogsrå&lt;/a&gt; is a library for loading configuration variables with ease, providing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variable defaults.&lt;/li&gt;
&lt;li&gt;Automatic type casting of values.&lt;/li&gt;
&lt;li&gt;Automatic docs and spec generation.&lt;/li&gt;
&lt;li&gt;OS environment template generation.&lt;/li&gt;
&lt;li&gt;Run-time reloading.&lt;/li&gt;
&lt;li&gt;Setting variable's values at run-time.&lt;/li&gt;
&lt;li&gt;Fast cached values access by using &lt;code&gt;:persistent_term&lt;/code&gt; as temporal storage.&lt;/li&gt;
&lt;li&gt;YAML configuration provider for Elixir releases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The previous example can be re-written as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Myapp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="s2"&gt;"My app config."&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Skogsra&lt;/span&gt;

  &lt;span class="nv"&gt;@envdoc&lt;/span&gt; &lt;span class="s2"&gt;"My hostname"&lt;/span&gt;
  &lt;span class="n"&gt;app_env&lt;/span&gt; &lt;span class="ss"&gt;:hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:myapp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;default:&lt;/span&gt; &lt;span class="s2"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;os_env:&lt;/span&gt; &lt;span class="s2"&gt;"HOSTNAME"&lt;/span&gt;

  &lt;span class="nv"&gt;@envdoc&lt;/span&gt; &lt;span class="s2"&gt;"My port"&lt;/span&gt;
  &lt;span class="n"&gt;app_env&lt;/span&gt; &lt;span class="ss"&gt;:port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:myapp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;default:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;os_env:&lt;/span&gt; &lt;span class="s2"&gt;"PORT"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This module will have these functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Myapp.Config.hostname/0&lt;/code&gt; for retrieving the hostname.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Myapp.Config.port/0&lt;/code&gt; for retrieving the port.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this implementation, we end up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documented configuration variables: Via &lt;code&gt;@envdoc&lt;/code&gt; module attribute.&lt;/li&gt;
&lt;li&gt;Easy to find: Every configuration variable will be in &lt;code&gt;Myapp.Config&lt;/code&gt; module.&lt;/li&gt;
&lt;li&gt;Easy to read: &lt;code&gt;app_env&lt;/code&gt; options are self explanatory.&lt;/li&gt;
&lt;li&gt;Declarative: we're telling Skogsrå &lt;em&gt;what we want&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bonus&lt;/strong&gt;: Type-safety (see Strong typing section).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/wAxlCmeX1ri1y/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/wAxlCmeX1ri1y/giphy.gif" alt="dance"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Calling &lt;code&gt;Myapp.Config.port()&lt;/code&gt; will retrieve the value for the port in the following order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;From the OS environment variable &lt;code&gt;$PORT&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From the configuration file e.g. our test config file might look like:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# file config/test.exs&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:myapp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;port:&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;From the default value, if it exists (In this case, it would return the integer &lt;code&gt;80&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The values will be casted as the default values' type unless the option &lt;code&gt;type&lt;/code&gt; is provided (see Explicit type casting section).&lt;/p&gt;

&lt;p&gt;Though Skogsrå has &lt;a href="https://github.com/gmtprime/skogsra"&gt;many options and features&lt;/a&gt;, we will just explore the ones I use the most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Explicit type casting.&lt;/li&gt;
&lt;li&gt;
Defining custom types.&lt;/li&gt;
&lt;li&gt;
Required variables.&lt;/li&gt;
&lt;li&gt;
Strong typing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Explicit type casting
&lt;/h2&gt;

&lt;p&gt;When the types are not &lt;code&gt;any&lt;/code&gt;, &lt;code&gt;binary&lt;/code&gt;, &lt;code&gt;integer&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt; or &lt;code&gt;atom&lt;/code&gt;, Skogsrå cannot automatically cast values solely by the default value's type. Types then need to be specified explicitly using the option &lt;code&gt;type&lt;/code&gt;. The available types are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:any&lt;/code&gt; (default).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:binary&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:integer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:float&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:boolean&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:atom&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:module&lt;/code&gt;: for modules loaded in the system.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:unsafe_module&lt;/code&gt;: for modules that might or might not be loaded in the system.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Skogsra.Type&lt;/code&gt; implementation: a &lt;code&gt;behaviour&lt;/code&gt; for defining custom types.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Defining custom types
&lt;/h2&gt;

&lt;p&gt;Let's say we need to read an OS environment variable called &lt;code&gt;HISTOGRAM_BUCKETS&lt;/code&gt; as a list of integers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HISTOGRAM_BUCKETS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1, 10, 30, 60"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We could then implement &lt;code&gt;Skogsra.Type&lt;/code&gt; behaviour to parse the string correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Myapp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;IntegerList&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Skogsra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;Skogsra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;~r/,/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trim&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="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_integer&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="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list&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="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;is_integer&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="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="ss"&gt;:error&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="ss"&gt;:error&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And finally use &lt;code&gt;Myapp.Type.IntegerList&lt;/code&gt; in our Skogsrå configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Myapp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Skogsra&lt;/span&gt;

  &lt;span class="nv"&gt;@envdoc&lt;/span&gt; &lt;span class="s2"&gt;"Histogram buckets"&lt;/span&gt;
  &lt;span class="n"&gt;app_env&lt;/span&gt; &lt;span class="ss"&gt;:buckets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:myapp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:histogram_buckets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="no"&gt;Myapp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;IntegerList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;os_env:&lt;/span&gt; &lt;span class="s2"&gt;"HISTOGRAM_BUCKETS"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then it should be easy to retrieve our &lt;code&gt;buckets&lt;/code&gt; from an OS environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"HISTOGRAM_BUCKETS"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="s2"&gt;"1, 10, 30, 60"&lt;/span&gt;
&lt;span class="n"&gt;iex&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Myapp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buckets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&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;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;or if the variable is not defined, from our application configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:myapp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:histogram_buckets&lt;/span&gt;&lt;span class="p"&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;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;iex&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Myapp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buckets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&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;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Required variables
&lt;/h2&gt;

&lt;p&gt;Skogsrå provides an option for making configuration variables mandatory. This is useful when there is no default value for our variable and Skogsrå it's expected to find a value in either an OS environment variable or the application configuration e.g. given the following config module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Skogsra&lt;/span&gt;

  &lt;span class="nv"&gt;@envdoc&lt;/span&gt; &lt;span class="s2"&gt;"Server port."&lt;/span&gt;
  &lt;span class="n"&gt;app_env&lt;/span&gt; &lt;span class="ss"&gt;:port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:myapp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;os_env:&lt;/span&gt; &lt;span class="s2"&gt;"PORT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;required:&lt;/span&gt; &lt;span class="no"&gt;true&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 function &lt;code&gt;Myapp.Config.port()&lt;/code&gt; will error if &lt;code&gt;PORT&lt;/code&gt; is undefined and&lt;br&gt;
the application configuration is not found:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"PORT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="n"&gt;iex&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:myapp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="n"&gt;iex&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Variable port in app myapp is undefined"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Strong typing
&lt;/h2&gt;

&lt;p&gt;All the configuration variables will have the correct function &lt;code&gt;@spec&lt;/code&gt; definition e.g. given the following definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Myapp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Skogsra&lt;/span&gt;

  &lt;span class="nv"&gt;@envdoc&lt;/span&gt; &lt;span class="s2"&gt;"PostgreSQL hostname"&lt;/span&gt;
  &lt;span class="n"&gt;app_env&lt;/span&gt; &lt;span class="ss"&gt;:db_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:myapp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:postgres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:port&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;default:&lt;/span&gt; &lt;span class="mi"&gt;5432&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 generated function &lt;code&gt;Myapp.Config.db_port/0&lt;/code&gt; will have the following &lt;code&gt;@spec&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="nv"&gt;@spec&lt;/span&gt; &lt;span class="n"&gt;db_port&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;::&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;integer&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="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The type is derived from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;default&lt;/code&gt; value (in this case the integer &lt;code&gt;5432&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;type&lt;/code&gt; configuration value (see the previous Explicit type casting section).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;a href="https://github.com/gmtprime/skogsra"&gt;Skogsra&lt;/a&gt; provides a simple way to handle your Elixir application configurations in a type-safe and organized way. Big projects can certainly benefit from using it.&lt;/p&gt;

&lt;p&gt;Hope you found this article useful. Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/PiQejEf31116URju4V/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/PiQejEf31116URju4V/giphy.gif" alt="coding"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article is also available here: &lt;a href="https://thebroken.link/skogsra-simplifying-your-elixir-configuration/"&gt;https://thebroken.link/skogsra-simplifying-your-elixir-configuration/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover image by &lt;a href="https://unsplash.com/@szmigieldesign?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Lukasz Szmigiel&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>configuration</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
