<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Elixir-and-Erlang | AkitaOnRails.com</title><link>https://akitaonrails.github.io/en/tags/elixir-and-erlang/</link><description>Fabio Akita's blog — tech, career, and assorted geek topics. English edition.</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 19 Oct 2026 23:20:51 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/en/tags/elixir-and-erlang/index.xml" rel="self" type="application/rss+xml"/><item><title>Are There Better Languages for Programming with LLMs?</title><link>https://akitaonrails.github.io/en/2026/10/11/are-there-better-languages-for-programming-with-llms/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/10/11/are-there-better-languages-for-programming-with-llms/</guid><pubDate>Sun, 11 Oct 2026 21:00:00 -0300</pubDate><description>&lt;p&gt;Last week DHH decided to farm engagement with the laziest format there is: he converted Campfire, his chat app, to several languages using AI agents, and started dropping the throughput results one by one on X. In the series he posted, the Rust version came out ahead of the Elixir and Ruby equivalents. The whole thread was designed to provoke, and it provoked.&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-partner="tweetdeck"&gt;&lt;p lang="en" dir="ltr"&gt;Last conversion of the Campfire from my token stash goes to JavaScript/Express. As with the others, I'm sure you can find more to optimize. But this gives you a sense of what order of magnitude these options exist within with agent-led development. https://t.co/UUapL99SJE https://t.co/2H2NXfeZog&lt;/p&gt;</description><content:encoded><![CDATA[<p>Last week DHH decided to farm engagement with the laziest format there is: he converted Campfire, his chat app, to several languages using AI agents, and started dropping the throughput results one by one on X. In the series he posted, the Rust version came out ahead of the Elixir and Ruby equivalents. The whole thread was designed to provoke, and it provoked.</p>
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr">Last conversion of the Campfire from my token stash goes to JavaScript/Express. As with the others, I'm sure you can find more to optimize. But this gives you a sense of what order of magnitude these options exist within with agent-led development. https://t.co/UUapL99SJE https://t.co/2H2NXfeZog</p>&mdash; DHH (@dhh) <a href="https://twitter.com/dhh/status/2107077808182976817">October 5, 2026</a></blockquote>
<p>I won&rsquo;t defend him this time. A one-shot conversion, done by an agent, with no fine-tuning at all, doesn&rsquo;t measure language: it measures how much the model knows each stack&rsquo;s idioms. It&rsquo;s rage bait and he knows it. That said, I do the same from time to time, and so do half his critics, so we&rsquo;re all hypocrites and life goes on.</p>
<p>And the weak spot of his methodology became a topic of its own: several people noticed the Rust version had gone through far more agent iterations than the others, meaning more optimization rounds. @kurtome answered in practice: he took the Elixir version and sped up the main page requests 10x, with obvious optimizations that no first conversion does, like caching and async database connection pooling.</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">@dhh Here you go https://t.co/gUtK7Z7zG7<br><br>got a 10x speed up on the Room page requests, and sped up all the other requests significantly as well. the biggest gains came from some no brainers: implement caching and pool the db connections with proper async https://t.co/mbp94q9N0s</p>&mdash; kurt (.com) (@kurtome) <a href="https://twitter.com/kurtome/status/2106902762076467681">October 5, 2026</a></blockquote>
<p>His comparison, then, wasn&rsquo;t &ldquo;Rust versus Elixir&rdquo;: it was &ldquo;Rust after N tuning rounds versus Elixir after one.&rdquo; And that lays bare the truth the thread pretends to ignore: <strong>no real software ships from half a dozen prompts</strong>. Coding with AI shines when an experienced engineer steers the agent toward their goal, and that means dozens of iterations, not three.</p>
<p>The good part is that the bait was useful to me. I&rsquo;ve been doing <a href="/en/2026/09/15/new-llm-benchmark-v4-retesting-all-top-llms-part-1/">LLM benchmarks</a> and <a href="/en/2026/10/10/which-jev-like-ai-decision-classifier-model-is-best/">decision-model benchmarks</a>, but I had never measured, in a reproducible way, the question that thread pretended to answer: <strong>does it matter which language you pick when the one writing the code is an LLM?</strong> My short answer was already out in two posts of mine:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Pressure under load is not a trivial problem to solve from scratch and I recommend you do not attempt to. It's a solved problem already and all solutions converge to what Elixir already brings out of the box. So yes. I use Rust all the time, but if I had to solve high scalability problems (particularly high volume message queues), I would think of Elixir first.</p>&mdash; Fabio Akita (@AkitaOnRails) <a href="https://twitter.com/AkitaOnRails/status/2108587462595367353">October 9, 2026</a></blockquote>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Language wars are tiresome. And the reality is simpler if you're doing AI assisted coding.<br><br>- system level (drivers, fs, daemons, etc): Rust. C++ is you're extending existing legacy<br>- windows only desktop apps: C#<br>- iOS/MacOS/iPadOS: Swift<br>- multi-OS: Rust/Tauri, Qt/C++, Flutter, Electron (pick your poison)<br>- simple websites: Node/Typescript/Bun/Deno<br>- highly scalable backend: Elixir, parts in Rust, parts in Go<br>- CLI tools: Rust<br><br>Everything else, by default, Rust. Unless it's an edge case.</p>&mdash; Fabio Akita (@AkitaOnRails) <a href="https://twitter.com/AkitaOnRails/status/2108590592263352540">October 9, 2026</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2>There&rsquo;s no better language, there&rsquo;s the right tool<span class="hx:absolute hx:-mt-20" id="theres-no-better-language-theres-the-right-tool"></span>
    <a href="#theres-no-better-language-theres-the-right-tool" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>That list from the post isn&rsquo;t a ranking, it&rsquo;s a terrain map. Every language and every framework was designed for a use case, and none covers them all. That was true when you wrote every line by hand and it stays true with agents. The LLM doesn&rsquo;t change the physics of the problem: it only changes who&rsquo;s typing.</p>
<p>And the map goes beyond language. A real system has infrastructure around it: Kubernetes when scale asks for it, SQLite by default because most apps never need more than that, Postgres with pgBouncer when they do, Redis for cache and key-value, RabbitMQ or NATS for queues. Picking &ldquo;the stack&rdquo; on day zero and expecting it to last ten years is fantasy. It doesn&rsquo;t, and that&rsquo;s fine.</p>
<p>Personal example: new web app, I open Ruby on Rails without thinking twice. It&rsquo;s the most complete end-to-end solution there is for the web, and with an LLM writing most of the code, the framework&rsquo;s productivity weighs even more than the language&rsquo;s. As the system grows, I swap the parts that ask for another tool: a high-volume queue leaves Rails&rsquo; side and goes to something from Erlang land, starting with RabbitMQ, which already solves the hard backpressure part for me.</p>
<blockquote>
  <p>No good software is finished: it grows. Usage dictates what you replace, rewrite and add. The final architecture is never the first commit&rsquo;s, and whoever tries to get everything right in the first commit only produces dead documents.</p>

</blockquote>
<h2>The token superstition<span class="hx:absolute hx:-mt-20" id="the-token-superstition"></span>
    <a href="#the-token-superstition" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>DHH&rsquo;s thread resurrected two beliefs that have been circulating since LLMs became work tools:</p>
<ul>
<li><strong>&ldquo;Script languages are better for AI because the lean syntax spends fewer tokens.&rdquo;</strong></li>
<li><strong>&ldquo;Strongly typed languages are better for AI because the compiler catches the errors, so you need less testing and less repair.&rdquo;</strong></li>
</ul>
<p>Both sound reasonable and both are superstition until someone measures. I was betting against both, from experience: a good compiler helps, but it doesn&rsquo;t do magic, and syntax conciseness was never the bottleneck of an agent that reads and writes files all day.</p>
<p>What really separates a project that moves from a project that spins in circles is test discipline. I&rsquo;ve written that <a href="/en/2026/04/20/clean-code-for-ai-agents/">TDD became a technical obligation, not a philosophy</a>, and this post&rsquo;s benchmark is one more data point in the same direction. It doesn&rsquo;t matter whether you do test-first or test-after: what matters is that the suite grows with the code, that <a href="/en/2026/03/01/software-is-never-done-4-projects-life-after-deploy-one-shot-prompt-myth/">every user-reported bug becomes a regression test</a> before the fix, and that you never make the same mistake twice. Software without users is dead software, so <a href="/en/2026/09/22/stop-making-excuses-and-ship-more-the-premise-changed/">release early, release often</a>, break fast and fix even faster. AI closes exactly that gap: the reproduce-test-fix cycle got cheap. QA became the programmer&rsquo;s most valuable skill, far above &ldquo;knowing Rust syntax.&rdquo;</p>
<p>Discipline beats compiler and beats language. That was my hypothesis. Time to measure.</p>
<h2>The benchmark<span class="hx:absolute hx:-mt-20" id="the-benchmark"></span>
    <a href="#the-benchmark" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I built a reproducible experiment, public in the <a href="https://github.com/akitaonrails/vibe-coding-benchmarks"target="_blank" rel="noopener">vibe-coding-benchmarks repository</a>, with per-attempt results published in JSON and the raw traces kept out of the repo for credential containment. It&rsquo;s not a language championship, and the design itself says so: it&rsquo;s a snapshot of one real task, measuring what can be honestly measured.</p>
<h3>The task<span class="hx:absolute hx:-mt-20" id="the-task"></span>
    <a href="#the-task" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>One task only, on purpose: maintain a multi-tenant <strong>ledger engine</strong>. The agent receives working v1 code and has to take it to a v2 contract, with per-tenant identities, authorization, serializable snapshots, a unique secondary index with alias swap, a capacity-bounded journal, torn-group recovery, checkpointing, legacy schema migration and durable retry IDs. It&rsquo;s not FizzBuzz: it&rsquo;s the kind of system that demands invariants across operations, exactly where LLMs tend to trip.</p>
<p>Correctness is judged by <strong>43 test stories applied after collection</strong>, out of the agent&rsquo;s sight: the 27 public ones from T1 plus 16 hidden ones. Passing is 43 out of 43. The conditions:</p>
<ul>
<li><strong>T0:</strong> prose contract, input and output examples, a working launcher. No test suite.</li>
<li><strong>T1:</strong> the same, plus 27 public stories and a driver to run them.</li>
</ul>
<p>Each attempt had a 15-minute budget, with at most two 3-minute continuations. In practice, none of the 112 needed a continuation.</p>
<h3>The languages and the models<span class="hx:absolute hx:-mt-20" id="the-languages-and-the-models"></span>
    <a href="#the-languages-and-the-models" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Seven languages in three strata, with pinned toolchains and Rust in safe mode:</p>
<ul>
<li><strong>Typed natives:</strong> Rust (safe, pinned), Haskell (pinned GHC).</li>
<li><strong>Typed VMs:</strong> C# (with nullable analysis), Kotlin, TypeScript (<code>strict</code>, <code>noUncheckedIndexedAccess</code>, <code>exactOptionalPropertyTypes</code>).</li>
<li><strong>Dynamic scripts:</strong> Python and Ruby, no checker at all.</li>
</ul>
<p>Four model configurations, all on subscriptions, no paid fallback: <strong>Kimi K3</strong> (high), <strong>GLM 5.3</strong> via OpenCode (high), <strong>Claude Opus 5.5</strong> (medium) and <strong>Codex with gpt-6 Sol</strong> (medium). Crossing everything: 7 languages × 4 models × 2 conditions × 2 repetitions = <strong>112 attempts</strong>.</p>
<h3>What this design doesn&rsquo;t measure<span class="hx:absolute hx:-mt-20" id="what-this-design-doesnt-measure"></span>
    <a href="#what-this-design-doesnt-measure" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Before the numbers, the brakes, because they&rsquo;re part of the result. It&rsquo;s a single task, with two repetitions per cell: a small sample by construction. Token coverage is partial in some models (missing data is not zero). The harness changed mid-campaign across three instrumentation epochs. And provider delays contaminate wall time: one of the Opus attempts spent half the clock waiting for quota release. All numbers below are descriptive, and I point out where they can&rsquo;t support a conclusion.</p>
<p>There&rsquo;s one additional brake, on purpose: <strong>the iteration level</strong>. My design measures what can be reached in all languages with similar iteration levels, on a short budget, with no human tuning afterwards. It&rsquo;s a photograph of the floor, not the ceiling. Any of the 112 implementations probably improves with more agent rounds, and the four queue stacks likewise: what I publish is what each language delivers &ldquo;out of the factory&rdquo; under the same effort, not the maximum a patient engineer would extract from it.</p>
<blockquote>
  <p><strong>Mandatory disclaimer before quoting any number from this post around:</strong> every result that follows holds for <strong>this</strong> benchmark, with <strong>this</strong> task, <strong>these</strong> models, <strong>these</strong> prompts and <strong>this</strong> hardware. Your case may vary violently. If your task doesn&rsquo;t look like a ledger engine, if your model isn&rsquo;t one of these four, if your queue isn&rsquo;t my queue, the numbers don&rsquo;t transfer. The code, the tasks, the grades and the step-by-step to reproduce are all in the <a href="https://github.com/akitaonrails/vibe-coding-benchmarks"target="_blank" rel="noopener">GitHub repository</a>. And if you disagree with some result and that disagreement matters to your company or your project, the only honest attitude is the one I had: <strong>stop arguing and run your own benchmark.</strong></p>

</blockquote>
<h2>Result 1: on correctness, the frontier models tied at the ceiling<span class="hx:absolute hx:-mt-20" id="result-1-on-correctness-the-frontier-models-tied-at-the-ceiling"></span>
    <a href="#result-1-on-correctness-the-frontier-models-tied-at-the-ceiling" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><table>
  <thead>
      <tr>
          <th>Configuration</th>
          <th style="text-align: right">Correct within budget</th>
          <th style="text-align: right">Correct at the limit</th>
          <th style="text-align: right">Failures</th>
          <th style="text-align: right">Ungraded</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Kimi K3 high</td>
          <td style="text-align: right">17</td>
          <td style="text-align: right">7</td>
          <td style="text-align: right">2</td>
          <td style="text-align: right">2</td>
      </tr>
      <tr>
          <td>GLM 5.3 high (OpenCode)</td>
          <td style="text-align: right">7</td>
          <td style="text-align: right">9</td>
          <td style="text-align: right">7</td>
          <td style="text-align: right">5</td>
      </tr>
      <tr>
          <td>Opus 5.5 medium</td>
          <td style="text-align: right">25</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">3 (infra interruption)</td>
          <td style="text-align: right">0</td>
      </tr>
      <tr>
          <td>Codex / Sol medium</td>
          <td style="text-align: right">28</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">0</td>
      </tr>
  </tbody>
</table>
<p>Reading: Sol passed all 28 eligible attempts and Opus its 25 (the 3 failures were infrastructure, not code). <strong>53 of 53 eligible attempts correct between the two.</strong> The task, made to be hard, was too easy to separate the frontier models, let alone the languages: with everyone passing everything, there&rsquo;s no correctness ranking to publish.</p>
<p>The difference showed up in the weaker models, and in the direction my hypothesis predicted: <strong>provided tests helped</strong>. Kimi and GLM, with the public suite in hand (T1), delivered 24 correct artifacts against 16 without it (T0). Kimi, in particular, passed all 14 attempts with provided tests. It&rsquo;s descriptive, not causal, but it&rsquo;s the cleanest signal in the whole benchmark: what changed the outcome wasn&rsquo;t the language, it was the presence of tests.</p>
<p>Per language, within each model, correctness scattered between 1/4 and 4/4 with no pattern that repeats across models. The three semantic failures I audited in depth are instructive: a GLM C# and a Kimi Python lost the same story by violating the same cross-operation invariant, and a Kimi Haskell missed a checkpoint path. None of the failures was a type error.</p>
<blockquote>
  <p>Where the model erred, it erred on domain invariants: the kind of error no compiler catches, in typed or dynamic languages. The myth that strong typing spares you the work of understanding the problem dies right there.</p>

</blockquote>
<h2>Result 2: concise language saved no tokens<span class="hx:absolute hx:-mt-20" id="result-2-concise-language-saved-no-tokens"></span>
    <a href="#result-2-concise-language-saved-no-tokens" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Here&rsquo;s the table that kills the first superstition. Medians per language on Sol, four valid attempts per cell:</p>
<table>
  <thead>
      <tr>
          <th>Language</th>
          <th style="text-align: right">Median time</th>
          <th style="text-align: right">Median tokens</th>
          <th style="text-align: right">Final code lines</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>TypeScript</td>
          <td style="text-align: right">53 s</td>
          <td style="text-align: right">147.5 thousand</td>
          <td style="text-align: right">~280</td>
      </tr>
      <tr>
          <td>Python</td>
          <td style="text-align: right">77 s</td>
          <td style="text-align: right">138.2 thousand</td>
          <td style="text-align: right">~300</td>
      </tr>
      <tr>
          <td>C#</td>
          <td style="text-align: right">61 s</td>
          <td style="text-align: right">171.6 thousand</td>
          <td style="text-align: right">~260</td>
      </tr>
      <tr>
          <td>Kotlin</td>
          <td style="text-align: right">65 s</td>
          <td style="text-align: right">170.6 thousand</td>
          <td style="text-align: right">~210</td>
      </tr>
      <tr>
          <td>Haskell</td>
          <td style="text-align: right">75 s</td>
          <td style="text-align: right">184.7 thousand</td>
          <td style="text-align: right">~265</td>
      </tr>
      <tr>
          <td>Ruby</td>
          <td style="text-align: right">82 s</td>
          <td style="text-align: right">185.6 thousand</td>
          <td style="text-align: right">~380</td>
      </tr>
      <tr>
          <td>Rust</td>
          <td style="text-align: right">80 s</td>
          <td style="text-align: right">188.1 thousand</td>
          <td style="text-align: right">~650</td>
      </tr>
  </tbody>
</table>
<p>Ruby, the most concise in lines (a bit over half of Rust&rsquo;s), cost practically the same tokens as Rust on Sol, and on Opus it was <strong>the most expensive of all</strong>, about 205 thousand median tokens against C#&rsquo;s 94 thousand. The cheapest were TypeScript and Python on Sol, C#, Haskell and Kotlin on Opus. If &ldquo;fewer source code lines = fewer LLM tokens&rdquo; were a law, Ruby should have led by a wide margin. It finished last.</p>
<p>And the second superstition? I audited the traces looking for the compiler-guided repair cycles that strong typing supposedly avoids. Result: <strong>zero compilation-error repair cycles</strong> in the audited Opus attempts, across Ruby, Rust and Haskell. Rust needed no compiler-driven fix at all; Haskell had one warning cleanup; Ruby spent its extra tokens on voluntary performance tuning. The frontier model almost always writes code that compiles on the first try, and the real effort goes to verification, not to appeasing the compiler.</p>
<p>One counterpoint that closes the coffin on the &ldquo;cheap is good&rdquo; thesis: GLM&rsquo;s <strong>lowest</strong> token-consumption cells were the failures. TypeScript T0 at 50 thousand tokens looks efficient until you see it&rsquo;s two unmodified starters because the model blew the output limit. Low tokens can mean early surrender.</p>
<h2>Result 3: under load, everyone tied at the queue<span class="hx:absolute hx:-mt-20" id="result-3-under-load-everyone-tied-at-the-queue"></span>
    <a href="#result-3-under-load-everyone-tied-at-the-queue" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Now for DHH&rsquo;s question: Rust &ldquo;beats&rdquo; Elixir and Ruby when the subject is throughput. I built the separate track for that: four queue workers against a real RabbitMQ, written with agent help, each in its natural idiom:</p>
<ul>
<li><strong>Elixir</strong> with OTP and Broadway</li>
<li><strong>Rust</strong> with Tokio and Lapin</li>
<li><strong>Rails</strong> with Bunny</li>
<li><strong>Plain Ruby</strong> with Bunny (control)</li>
</ul>
<p>A generator published 10 thousand 4 KB messages to RabbitMQ, at 200 per second, and each worker consumed as fast as it could, hitting the effects on an HTTP/SQLite collector with a nominal 50 ms delay, limited to 2 CPUs and 1 GB of RAM. Two concurrency profiles:</p>
<table>
  <thead>
      <tr>
          <th>Stack</th>
          <th style="text-align: right">Handlers</th>
          <th style="text-align: right">Effects/s</th>
          <th style="text-align: right">Correct/10,000</th>
          <th style="text-align: right">Memory peak</th>
          <th>Drained?</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Elixir</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">94.8</td>
          <td style="text-align: right">10,000</td>
          <td style="text-align: right">135 MiB</td>
          <td>yes</td>
      </tr>
      <tr>
          <td>Rust</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">94.6</td>
          <td style="text-align: right">10,000</td>
          <td style="text-align: right">16 MiB</td>
          <td>yes</td>
      </tr>
      <tr>
          <td>Rails</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">92.7</td>
          <td style="text-align: right">10,000</td>
          <td style="text-align: right">79 MiB</td>
          <td>yes</td>
      </tr>
      <tr>
          <td>Ruby</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">92.5</td>
          <td style="text-align: right">10,000</td>
          <td style="text-align: right">38 MiB</td>
          <td>yes</td>
      </tr>
      <tr>
          <td>Elixir</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">84.8</td>
          <td style="text-align: right">7,915</td>
          <td style="text-align: right">161 MiB</td>
          <td>no</td>
      </tr>
      <tr>
          <td>Rust</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">88.2</td>
          <td style="text-align: right">8,151</td>
          <td style="text-align: right">23 MiB</td>
          <td>no</td>
      </tr>
      <tr>
          <td>Rails</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">88.5</td>
          <td style="text-align: right">8,108</td>
          <td style="text-align: right">87 MiB</td>
          <td>no</td>
      </tr>
      <tr>
          <td>Ruby</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">88.0</td>
          <td style="text-align: right">8,081</td>
          <td style="text-align: right">60 MiB</td>
          <td>no</td>
      </tr>
  </tbody>
</table>
<p>On the 8-handler profile, the four stacks delivered the same throughput, 93 to 95 effects per second, and drained the whole queue. The apparent bottleneck was the shared collector (a Python lock, a single SQLite writer), not the languages. On the 64-handler profile, all of them left a backlog and blew the client timeout en masse, so I marked those cells as not qualified: the strong protocol, with a group-commit collector and a 50 to 800 msg/s ramp, is designed but not executed.</p>
<p>The only difference left standing in the numbers was <strong>memory</strong>: Rust held the peak at 16 MiB where the BEAM spent 135. It&rsquo;s a real Rust advantage in density, with the caveat that a cgroup peak is not a language footprint. On throughput, which was the thread&rsquo;s claim, a technical tie.</p>
<p>And the tie deserves an honest reading: it may only mean my pressure was too little. With the shared collector holding everything around 95 effects per second, any stack capable of more than that shows up as &ldquo;equal.&rdquo; What this test shows is where the bottleneck was, not where each stack breaks. To find each one&rsquo;s ceiling I&rsquo;d need the strong protocol, with a group-commit collector and a load ramp, which is designed but not executed. Until then, &ldquo;all stacks perform the same&rdquo; is the wrong conclusion; &ldquo;all of them stayed above what my test could measure&rdquo; is the right one.</p>
<p>And the question that interested me most, &ldquo;does the agent work less to deliver a reliable queue consumer in Elixir than in Rust?&rdquo;, was deliberately left unanswered: these four implementations were written to validate the environment, not as a controlled coding attempt. Comparing AI effort between Elixir and Rust would require the same ledger matrix running on the queue task. It&rsquo;s on the list.</p>
<h2>So, does the language matter?<span class="hx:absolute hx:-mt-20" id="so-does-the-language-matter"></span>
    <a href="#so-does-the-language-matter" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>With the data on the table, the thread&rsquo;s three questions can be answered:</p>
<p><strong>Is there a better language for programming with LLMs?</strong> None showed up. In the frontier models, correctness and effort varied less between languages than between models. Where there was a correctness difference, it followed the presence of provided tests, not syntax. &ldquo;No general language ranking is established&rdquo; is the report&rsquo;s official sentence, and it&rsquo;s the correct one.</p>
<p><strong>Do the token-economy theses hold?</strong> Neither one. Conciseness didn&rsquo;t save: Ruby cost as much as Rust, or more. Strong typing spared no repair: there was no type repair to spare. The models&rsquo; effort went to verification and understanding the contract, and that has no language.</p>
<p><strong>Was DHH right?</strong> In what he measured, he measured what an agent delivers for free on each stack, without tuning, and called that a verdict on language. In my test, with real load and honest measurement, the four stacks tied on throughput in the stable regime, and only memory differentiated Rust. If his thread proves anything, it proves that agent conversion measures the model, not the language.</p>
<p>My bets from the introduction came out standing: testing and discipline are the lever, the compiler is a supporting actor, and the right tool depends on the problem. New web app is still Rails in my hand, heavy queue is still Erlang territory, low-level system is still Rust, and the LLM writes any of them with the same competence. Whoever spent the week fighting for their favorite language on X fought over nothing.</p>
<p>And my daily practice tells the same story. I have a personal bias toward Rust and even so I&rsquo;ve published more than 30 open source projects in all kinds of stacks: <a href="https://github.com/akitaonrails/ai-memory"target="_blank" rel="noopener">ai-memory</a> is Rust, <a href="https://github.com/akitaonrails/FrankMD"target="_blank" rel="noopener">FrankMD</a> is Ruby on Rails, <a href="https://github.com/akitaonrails/ai-memory-web"target="_blank" rel="noopener">ai-memory-web</a> and <a href="https://github.com/akitaonrails/ai-jail-web"target="_blank" rel="noopener">ai-jail-web</a> are Node.js. In none of them did I feel any relevant difference that came from the stack: the wins were similar, the errors too, and the errors were always understandable. Every error became an invariant, a rule, a skill improvement. It never became a language switch.</p>
<p>So forget &ldquo;token savings&rdquo; and &ldquo;typing superstition.&rdquo; Do real software engineering: pick the right tool for your specific problem, apply proper engineering technique, and any reasonable agent will perform more or less the same. And stop wasting time arguing over language-war rage bait on the internet. Be a real software engineer and do real engineering. That&rsquo;s it.</p>
<p>The per-attempt data, the full methodology and the evidence limits are in the <a href="https://github.com/akitaonrails/vibe-coding-benchmarks"target="_blank" rel="noopener">repository</a>. If you run the matrix with another task and find a ranking I didn&rsquo;t, show me.</p>
]]></content:encoded><category>llm-benchmarks</category><category>programming-languages</category><category>llms</category><category>elixir-and-erlang</category></item><item><title>Ex Manga Downloadr - Part 7: Properly dealing with large Collections</title><link>https://akitaonrails.github.io/en/2017/06/16/ex-manga-downloadr-part-7-properly-dealing-with-large-collections/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2017/06/16/ex-manga-downloadr-part-7-properly-dealing-with-large-collections/</guid><pubDate>Fri, 16 Jun 2017 15:10:00 -0300</pubDate><description>&lt;p&gt;In my &lt;a href="http://www.akitaonrails.com/en/2017/06/13/ex-manga-downloadr-parte-6-a-ascensao-do-flow"target="_blank" rel="noopener"&gt;previous post&lt;/a&gt; I was able to simplify a lot of the original code through the use of &lt;a href="https://github.com/elixir-lang/flow"target="_blank" rel="noopener"&gt;Flow&lt;/a&gt;. But the downside is that the running time actually increased a lot.&lt;/p&gt;
&lt;p&gt;José Valim kindly stepped in and posted a &lt;a href="http://www.akitaonrails.com/en/2017/06/13/ex-manga-downloadr-parte-6-a-ascensao-do-flow#comment-3360301947"target="_blank" rel="noopener"&gt;valuable comment&lt;/a&gt;, which I will paste here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Have you tried reducing the &lt;code&gt;@max_demand&lt;/code&gt; instead? &lt;code&gt;@max_demand&lt;/code&gt; is how much data you exchange between stages. If you set it to 60, it means you are sending 60 items to one stage, 60 items to the other and so on. That gives you poor balancing for small collections as there is a chance all items end-up in the same stage. You actually want to reduce &lt;code&gt;max_demand&lt;/code&gt; to 1 or 2 so each stage gets small batches and request more than needed. Another parameter you usually tune is the &lt;code&gt;stages: ...&lt;/code&gt; option, you should set that to the amount of connections you had in poolboy in the past.&lt;/p&gt;</description><content:encoded><![CDATA[<p>In my <a href="http://www.akitaonrails.com/en/2017/06/13/ex-manga-downloadr-parte-6-a-ascensao-do-flow"target="_blank" rel="noopener">previous post</a> I was able to simplify a lot of the original code through the use of <a href="https://github.com/elixir-lang/flow"target="_blank" rel="noopener">Flow</a>. But the downside is that the running time actually increased a lot.</p>
<p>José Valim kindly stepped in and posted a <a href="http://www.akitaonrails.com/en/2017/06/13/ex-manga-downloadr-parte-6-a-ascensao-do-flow#comment-3360301947"target="_blank" rel="noopener">valuable comment</a>, which I will paste here:</p>
<blockquote>
  <p>Have you tried reducing the <code>@max_demand</code> instead? <code>@max_demand</code> is how much data you exchange between stages. If you set it to 60, it means you are sending 60 items to one stage, 60 items to the other and so on. That gives you poor balancing for small collections as there is a chance all items end-up in the same stage. You actually want to reduce <code>max_demand</code> to 1 or 2 so each stage gets small batches and request more than needed. Another parameter you usually tune is the <code>stages: ...</code> option, you should set that to the amount of connections you had in poolboy in the past.</p>

</blockquote>
<blockquote>
  <p>However, I believe you don&rsquo;t need to use Flow at all. Elixir v1.4 has something called <a href="https://hexdocs.pm/elixir/Task.html#async_stream/5"target="_blank" rel="noopener"><code>Task.async_stream</code></a> which is a mixture of poolboy + task async, which is a definitely better replacement to what you had. We have added it to Elixir after implementing Flow as we realized you can get a lot of mileage out of <code>Task.async_stream</code> without needing to jump to a full solution like Flow. If using <code>Task.async_stream</code>, the <code>max_concurrency</code> option controls your pool size.</p>

</blockquote>
<p>And, obviously, he is right. I misunderstood how Flow works. It&rsquo;s meant to be used when you have a bazillion items to process in parallel. Particularly processing units that can have high variance and, hence, a lot of back-pressure not only because there is a lot of items to process, but because their running times can vary dramatically. So, it&rsquo;s one of those cases of bringing a cannon to kill a fly.</p>
<p>What I wasn&rsquo;t aware is the existence of <code>Task.async_stream</code> and its companion <a href="https://hexdocs.pm/elixir/Task.Supervisor.html#async_stream/4"target="_blank" rel="noopener"><code>Task.Supervisor.async_stream</code></a> if I need to add more control.</p>
<p>Let&rsquo;s backtrack a bit.</p>
<h3>Dealing with collections in Elixir<span class="hx:absolute hx:-mt-20" id="dealing-with-collections-in-elixir"></span>
    <a href="#dealing-with-collections-in-elixir" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Erlang is a beast. It provides all the building blocks of a real-time, highly-concurrent, operating system! Really, what it provides out of the box is way more than any other language/platform/virtual machine, ever. You don&rsquo;t get that much for free on Java, or .NET or anything. You have to assemble the pieces manually, spend hundreds of hours tweaking, and still pray a lot to have everything working seamlessly.</p>
<p>So, you have distributed systems to build? There is no other option, really. Do Erlang, or suffer in hell.</p>
<p>Then, Elixir steps this up a notch creating a very reasonable and simple to use standard library that makes the coding part actually enjoyable. This is a killer combo. You need to do the next Whatsapp? You need to do the next Waze? You need to rebuild Cassandra from scratch? You need to create stuff like Apache Spark? Do Elixir.</p>
<p>In Erlang, you need to solve everything using GenServer. It&rsquo;s a neat abstraction from OTP. You are <a href="http://www.akitaonrails.com/en/en/2015/11/22/observando-processos-em-elixir-the-little-elixir-otp-guidebook/"target="_blank" rel="noopener">required to understand OTP</a> intimately. There is no shortcut here. There is no Erlang without OTP.</p>
<p>That said, you can start simple and scale without so much hassle.</p>
<p>Usually, everything starts with Collections, or more correctly, some kind of <a href="https://hexdocs.pm/elixir/Enum.html#content"target="_blank" rel="noopener">Enumeration</a>.</p>
<p>Just like my simple <code>Workflow.pages/1</code> function which iterates through a list of chapter links, fetch each link, parse the returning HTML and extracts the collection of page links within that chapter, reducing the sub-lists into a full list of page links.</p>
<p>If I know the collection is small (less than 100 items, for example) I would just do this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pages</span><span class="p">({</span><span class="n">chapter_list</span><span class="p">,</span> <span class="n">source</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">   <span class="n">pages_list</span> <span class="o">=</span> <span class="n">chapter_list</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Worker</span><span class="o">.</span><span class="n">chapter_page</span><span class="p">(</span><span class="o">[&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="n">source</span><span class="o">]</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="o">[]</span><span class="p">,</span> <span class="n">fn</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">list</span><span class="p">},</span> <span class="n">acc</span> <span class="o">-&gt;</span> <span class="n">acc</span> <span class="o">++</span> <span class="n">list</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="n">pages_list</span><span class="p">,</span> <span class="n">source</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And that&rsquo;s it. This is linear. It will sequentially process just one link at a time. The more chapter links, the longer it will take. Usually I want to process this in parallel. But I can&rsquo;t fire a parallel process for each chapter link, because if I receive 1,000 chapter links and fire them all, it will be a Denial of Service and I will certainly receive hundreds of time outs.</p>
<p>You can run into 2 main problems when you need to iterate through a big collection.</p>
<ul>
<li>
<p>If your collection is humongous (imagine a gigabyte long text file that you need to iterate line by line). For that you use <a href="https://hexdocs.pm/elixir/Stream.html#content"target="_blank" rel="noopener"><code>Stream</code></a> instead of <code>Enum</code>. All functions look almost exactly the same, but you will not have to load the entire collection into memory and you will not keep duplicating it.</p>
</li>
<li>
<p>If your processing unit takes a long time. Now that you solved not blowing off your memory usage, what if you have slow jobs while iterating through each item in the collection? That&rsquo;s our case, where the collection is rather small, but the processing time is long as it&rsquo;s fetching from an external source on the internet. It can take milisseconds, it can take a few seconds.</p>
</li>
</ul>
<p>One way to control this is through the use of &ldquo;batches&rdquo;, something along these lines:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pages</span><span class="p">({</span><span class="n">chapter_list</span><span class="p">,</span> <span class="n">source</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">pages_list</span> <span class="o">=</span> <span class="n">chapter_list</span>
</span></span><span class="line"><span class="cl">	<span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">chunk</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="n">pages_download</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="n">source</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">	<span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">concat</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">pages_list</span><span class="p">,</span> <span class="n">source</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pages_download</span><span class="p">(</span><span class="n">chapter_list</span><span class="p">,</span> <span class="n">source</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="n">results</span> <span class="o">=</span> <span class="n">chapter_list</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Task</span><span class="o">.</span><span class="n">async</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span> <span class="no">Worker</span><span class="o">.</span><span class="n">chapter_page</span><span class="p">(</span><span class="o">[&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="n">source</span><span class="o">]</span><span class="p">)</span> <span class="k">end</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Task</span><span class="o">.</span><span class="n">await</span><span class="o">/</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="o">[]</span><span class="p">,</span> <span class="n">fn</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">list</span><span class="p">},</span> <span class="n">acc</span> <span class="o">-&gt;</span> <span class="n">acc</span> <span class="o">++</span> <span class="n">list</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="n">pages_list</span><span class="p">,</span> <span class="n">source</span><span class="p">},</span> <span class="n">results</span>	
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is just for the example, I have not compiled this snippet to see if it works, but you get the idea of chunking the big list and processing each chunk through <code>Task.async</code> and <code>Task.await</code>.</p>
<p>Again, for small lists, this should be ok (thousands) and where each item does not take too much to process.</p>
<p>Now, this is not very good. Because each chunk must finish before the next chunk begins. Which is why the ideal solution is to keep a constant amount of jobs running at any given time. To that end, we need a Pool, which is what I explained in <a href="http://www.akitaonrails.com/en/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate/"target="_blank" rel="noopener">Part 2: Poolboy to the rescue!</a>.</p>
<p>But implementing the proper way to keep the pool entirely filled requires some boring juggling between Poolboy transactions and <code>Task.Supervisor.async</code>. Which is why I was interested in the new <code>Flow</code> usage.</p>
<p>The code does come clean, but as I explained before, this is not the proper use case for Flow. It&rsquo;s better when you have to iterate over tens of thousands of items or even an infinite stream (you have incoming traffic of requests in need of parallel processing, for example).</p>
<p>So, finally, there is a compromise. The solution between the simple <code>Task.async</code> and <code>Flow</code> is <code>Task.async_stream</code> which works like a pool implementation, where it keeps a <code>max_concurrency</code> of jobs running in a stream. The final code becomes way more elegant like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pages</span><span class="p">({</span><span class="n">chapter_list</span><span class="p">,</span> <span class="n">source</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">pages_list</span> <span class="o">=</span> <span class="n">chapter_list</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Task</span><span class="o">.</span><span class="n">async_stream</span><span class="p">(</span><span class="no">MangaWrapper</span><span class="p">,</span> <span class="ss">:chapter_page</span><span class="p">,</span> <span class="o">[</span><span class="n">source</span><span class="o">]</span><span class="p">,</span> <span class="ss">max_concurrency</span><span class="p">:</span> <span class="vi">@max_demand</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">to_list</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="o">[]</span><span class="p">,</span> <span class="n">fn</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">list</span><span class="p">}},</span> <span class="n">acc</span> <span class="o">-&gt;</span> <span class="n">acc</span> <span class="o">++</span> <span class="n">list</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">pages_list</span><span class="p">,</span> <span class="n">source</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is the <a href="https://github.com/akitaonrails/ex_manga_downloadr/commit/517183261e998ab40f6e5bc793b4db9adcf899e3"target="_blank" rel="noopener">final commit</a> with the aforementioned changes.</p>
<h3>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The implementation with <code>Task.async_stream</code> is super simple and the times finally became the same as before.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>84,16s user 20,80s system 138% cpu 1:15,94 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Way better than the more than 3 minutes it was taking with Flow. And this is not because Flow is slow, it&rsquo;s because I was not using it correctly, probably shooting a big chunk into a single GenStage and creating a bottleneck. Again, only use Flow if you have enough items to put hundreds of them into several parallel GenStages. We are talking about collections with tens of thousands of items, not my meager pages list.</p>
<p>There is a small tweak though. To fetch all chapter and page links I am using a <code>max_concurrency</code> of 100. Timeout is the default at 5000 (5 seconds). That works because the returning HTML is not so big and we can parallelize that much on a high bandwidth connection.</p>
<p>But the images downloading procedure at <code>Workflow.process_downloads</code> I had to cut <code>max_concurrency</code> in half and increase the <code>timeout</code> up to 30 seconds to make sure it wouldn&rsquo;t crash.</p>
<p>Because this is a simple implementation there is no crash recovery and no retry routine. I would have to replace this implementation with <code>Task.Supervisor.async_stream</code> to regain some control. My original implementation was more cumbersome but I had places to add retry mechanisms. So again, it&rsquo;s a compromise between ease of use and control, always. The more control you have, the worse the code becomes.</p>
<p>This is a simple example exercise, so I will keep it at that for the time being.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>performance</category></item><item><title>Ex Manga Downloadr - Part 6: The Rise of FLOW</title><link>https://akitaonrails.github.io/en/2017/06/13/ex-manga-downloadr-part-6-the-rise-of-flow/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2017/06/13/ex-manga-downloadr-part-6-the-rise-of-flow/</guid><pubDate>Tue, 13 Jun 2017 15:59:00 -0300</pubDate><description>&lt;p&gt;It&amp;rsquo;s been way more than a year since I posted about my pet project &lt;a href="https://github.com/akitaonrails/ex_manga_downloadr"target="_blank" rel="noopener"&gt;Ex Manga Downloadr&lt;/a&gt;. Since then I just did small updates to keep up with the current Elixir and libraries versions.&lt;/p&gt;
&lt;p&gt;As a quick recap, the exercise is that I want to scrape MangaReader.net for a bunch of images, organized in pages and chapters, and in the end it should compile organized PDFs so I can load them on a Kindle device.&lt;/p&gt;</description><content:encoded><![CDATA[<p>It&rsquo;s been way more than a year since I posted about my pet project <a href="https://github.com/akitaonrails/ex_manga_downloadr"target="_blank" rel="noopener">Ex Manga Downloadr</a>. Since then I just did small updates to keep up with the current Elixir and libraries versions.</p>
<p>As a quick recap, the exercise is that I want to scrape MangaReader.net for a bunch of images, organized in pages and chapters, and in the end it should compile organized PDFs so I can load them on a Kindle device.</p>
<p>Web scraping is a simple loop of HTTP GETs over a ton of URLs, scraping the HTML, and fetching more URLs to download.</p>
<p>In many simple languages, people usually solve this naively in 2 ways:</p>
<ul>
<li>A simple nested loop. One single thread, sequential fetches. So if you have 5,000 links and each links takes 10 seconds to fetch, it&rsquo;s basically 10 * 5,000 = 50,000 seconds, which is a stupid long time.</li>
<li>A simple spawn of processes, fibers, threads or parallel I/O in a reactor, all at once. An attempt to parallelize all fetches at once.</li>
</ul>
<p>Everybody probably agrees that the first option is stupid. Now, the second one is tricky.</p>
<p>The tricky part is <strong>CONTROL</strong>.</p>
<p>Anyone in Go would say <em>&ldquo;oh, this is easy, just put a loop and spawn a bunch of goroutines&rdquo;</em> or anyone in Node.js would say <em>&ldquo;oh, this is easy, just put a loop, make the fetch - they all will run asynchronously - and add callbacks, a basic async/await.&rdquo;</em></p>
<p>They&rsquo;re not wrong, but this is still too naive an implementation. It&rsquo;s trivial to trigger hundreds or thousands of parallel requests. Now, what happens if one fails and you have to retry? What happens if the MangaReader has a throttling system that will either start cutting down connections or timing them out? Or if your internet bandwidth is just not enough, and after a certain amount of requests you start having diminishing returns and time outs?</p>
<p>The message is: it&rsquo;s damn trivial to spawn parallel stuff. it&rsquo;s damn complicated to control parallel stuff.</p>
<p>This is why, in my first implementation in Elixir, I introduced a complicated implementation using a combination of a custom GenServer, Elixir&rsquo;s own Task infrastructure for async/await pattern, and <a href="https://github.com/devinus/poolboy"target="_blank" rel="noopener">Poolboy</a> to control the rate of the parallelism. This is how you control the bottleneck out to slow resources: using pools and queues (which is why every good database has a connection pool, remember <a href="https://sourceforge.net/projects/c3p0/"target="_blank" rel="noopener">C3P0</a>?)</p>
<p>This is one snippet of my older implementation:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">chapter_page</span><span class="p">(</span><span class="o">[</span><span class="n">chapter_link</span><span class="p">,</span> <span class="n">source</span><span class="o">]</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Task</span><span class="o">.</span><span class="n">Supervisor</span><span class="o">.</span><span class="n">async</span><span class="p">(</span><span class="no">Fetcher</span><span class="o">.</span><span class="n">TaskSupervisor</span><span class="p">,</span> <span class="n">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:poolboy</span><span class="o">.</span><span class="n">transaction</span> <span class="ss">:worker_pool</span><span class="p">,</span> <span class="n">fn</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="p">{</span><span class="ss">:chapter_page</span><span class="p">,</span> <span class="n">chapter_link</span><span class="p">,</span> <span class="n">source</span><span class="p">},</span> <span class="vi">@genserver_call_timeout</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">,</span> <span class="vi">@task_async_timeout</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Yes, it&rsquo;s very ugly, and there are boilerplates for the GenServer, the custom Supervisor to initialize Poolboy and so on. And the higher level workflow code looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pages</span><span class="p">({</span><span class="n">chapter_list</span><span class="p">,</span> <span class="n">source</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">   <span class="n">pages_list</span> <span class="o">=</span> <span class="n">chapter_list</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Worker</span><span class="o">.</span><span class="n">chapter_page</span><span class="p">(</span><span class="o">[&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="n">source</span><span class="o">]</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Task</span><span class="o">.</span><span class="n">await</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="vi">@await_timeout_ms</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="o">[]</span><span class="p">,</span> <span class="n">fn</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">list</span><span class="p">},</span> <span class="n">acc</span> <span class="o">-&gt;</span> <span class="n">acc</span> <span class="o">++</span> <span class="n">list</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="n">pages_list</span><span class="p">,</span> <span class="n">source</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So, inside the <code>Worker</code> module each public method wraps the GenServer internal calls into a <code>Task async</code> and in the collection iteration we add <code>Task.await</code> to actually wait for all parallel calls to finish, so we can finally reduce the results.</p>
<p>Elixir now comes with this very interesting <a href="https://elixir-lang.org/blog/2016/07/14/announcing-genstage/"target="_blank" rel="noopener"><code>GenStage</code></a> infrastructure that promises to replace <code>GenEvent</code> and the use case is when you have a producer-consumer situation with back-pressure. Basically when you have slow endpoints and you would end up having to control bottlenecks.</p>
<p>Then, <a href="https://github.com/elixir-lang/flow"target="_blank" rel="noopener">Flow</a> is an easier high abstraction that you can use almost the same way you would use <code>Enum</code> in your collections, but instead of sequential mapping, it takes care of parallel traversing and control of batches. So the code is very similar but without you having to control the parallelization controls manually.</p>
<p>This is the <a href="https://github.com/akitaonrails/ex_manga_downloadr/commit/b117f5236098f6d37e332633acb787be46a09d84"target="_blank" rel="noopener">full commit</a> where I could remove Poolboy, remove my custom GenServer, reimplement the Worker as simple module of functions and then the workflow could get rid off the async/await pattern and use Flow instead:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pages</span><span class="p">({</span><span class="n">chapter_list</span><span class="p">,</span> <span class="n">source</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">   <span class="n">pages_list</span> <span class="o">=</span> <span class="n">chapter_list</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Flow</span><span class="o">.</span><span class="n">from_enumerable</span><span class="p">(</span><span class="ss">max_demand</span><span class="p">:</span> <span class="vi">@max_demand</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Flow</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">MangaWrapper</span><span class="o">.</span><span class="n">chapter_page</span><span class="p">(</span><span class="o">[&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="n">source</span><span class="o">]</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Flow</span><span class="o">.</span><span class="n">partition</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Flow</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span> <span class="o">[]</span> <span class="k">end</span><span class="p">,</span> <span class="n">fn</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">list</span><span class="p">},</span> <span class="n">acc</span> <span class="o">-&gt;</span> <span class="n">acc</span> <span class="o">++</span> <span class="n">list</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">to_list</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="n">pages_list</span><span class="p">,</span> <span class="n">source</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The only boilerplate left is the <code>Flow.from_enumerable()</code> and <code>Flow.partition()</code> wrapping the <code>Flow.map</code>, and that&rsquo;s it!</p>
<p>Notice I configured <code>@max_demand</code> to be 60. You must tweak it to be larger or smaller depending on your internet connection, you have to experiment it. By default, Flow will trigger 500 processes in parallel, which is way too much for web scraping on a normal home internet connection and you will suffer diminishing returns. That&rsquo;s what I had to do previously with Poolboy, by initiating a pool of around 60 transactions at most.</p>
<p>Unfortunately not everything is as straight forward as it seems. Running this new version on the test mode I get this result:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>58,85s user 13,93s system 37% cpu 3:13,78 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So a total time of more than 3 minutes, using around 37% of the available CPU.</p>
<p>My immediate previous version using all the shenanigans of Poolboy, Task.Supervisor, GenServer, etc still gives me this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>100,67s user 20,83s system 152% cpu 1:19,92 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Less than <strong>HALF</strong> the time, albeit using all my CPU cores. So my custom implementation still uses my resources to the maximum. There is still something in the Flow implementation I didn&rsquo;t quite get right. I already tried to bump up the <code>max_demand</code> from 60 up to 100 but that didn&rsquo;t improve anything. Leaving it to the default 500 slows everything down to more than 7 minutes.</p>
<p>All in all, at least it makes the implementation far easier on the eyes (hence, way easier to maintain), but either the Flow implementation has bottlenecks or I am using it wrong at this point. If you know what it is, let me know in the comments section below.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>performance</category><category>distributed-systems</category></item><item><title>Ex Pusher Lite - Part 3 - A Complete Solution</title><link>https://akitaonrails.github.io/en/2016/12/30/ex-pusher-lite-part-3-a-complete-solution/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/12/30/ex-pusher-lite-part-3-a-complete-solution/</guid><pubDate>Fri, 30 Dec 2016 17:38:00 -0200</pubDate><description>&lt;p&gt;It&amp;rsquo;s been over a year since I wrote the 2 pieces about my &amp;ldquo;Ex Pusher Lite&amp;rdquo; concept. The code from a year ago is already obsolete as I was still just learning my way through both Elixir and Phoenix.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve published an article about &lt;a href="http://www.akitaonrails.com/en/2016/12/06/coherence-e-exadmin-devise-e-activeadmin-para-phoenix"target="_blank" rel="noopener"&gt;ExAdmin and Coherence&lt;/a&gt; and another on &lt;a href="http://www.akitaonrails.com/en/2016/12/23/aplicativo-elixir-phoenix-deploy-balanceamento-carga-digitalocean"target="_blank" rel="noopener"&gt;Deploying Elixir to DigitalOcean&lt;/a&gt; this month.&lt;/p&gt;
&lt;p&gt;The idea is very simple, it is a homage to &lt;a href="https://pusher.com/"target="_blank" rel="noopener"&gt;Pusher&lt;/a&gt;. If you used Pusher before, this is very similar (albeit way less feature complete, of course).&lt;/p&gt;</description><content:encoded><![CDATA[<p>It&rsquo;s been over a year since I wrote the 2 pieces about my &ldquo;Ex Pusher Lite&rdquo; concept. The code from a year ago is already obsolete as I was still just learning my way through both Elixir and Phoenix.</p>
<p>I&rsquo;ve published an article about <a href="http://www.akitaonrails.com/en/2016/12/06/coherence-e-exadmin-devise-e-activeadmin-para-phoenix"target="_blank" rel="noopener">ExAdmin and Coherence</a> and another on <a href="http://www.akitaonrails.com/en/2016/12/23/aplicativo-elixir-phoenix-deploy-balanceamento-carga-digitalocean"target="_blank" rel="noopener">Deploying Elixir to DigitalOcean</a> this month.</p>
<p>The idea is very simple, it is a homage to <a href="https://pusher.com/"target="_blank" rel="noopener">Pusher</a>. If you used Pusher before, this is very similar (albeit way less feature complete, of course).</p>
<p>I built an entire solution inspired by Pusher, using the Phoenix framework, deployed to Digital Ocean and you can test it out right now, just sign up at <a href="http://expusherlite.cm42.io"target="_blank" rel="noopener">expusherlite.cm42.io</a>.</p>
<p>Once you sign up, you will have a secret token (don&rsquo;t disclose that, of course) and you will be under an Organization. Then you can go on and create Applications within that Organization. Each Application will have a unique token to identify it.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/576/expusherlite_cm42_io_registrations.png" alt="dashboard"  loading="lazy" /></p>
<p>Now, let&rsquo;s say you want to create a Rails application with a Chat feature. Any version of Rails, you don&rsquo;t need 5.0 and you don&rsquo;t need ActionCable.</p>
<p>First off, let&rsquo;s configure <code>config/secrets.yml</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>development:
  secret_key_base: b9a1...e7aa
  pusher_host: &#34;expusherlite.cm42.io&#34;
  org_id: acme-inc
  app_key: 0221...f193
  secret_token: 4036...f193
...
production:
  secret_key_base: &lt;%= ENV[&#34;SECRET_KEY_BASE&#34;] %&gt;
  pusher_host: &lt;%= ENV[&#39;PUSHER_LITE_HOST&#39;] %&gt;
  org_id: &lt;%= ENV[&#39;PUSHER_LITE_ORGANIZATION&#39;] %&gt;
  app_key: &lt;%= ENV[&#34;PUSHER_LITE_APP_KEY&#34;] %&gt;
  secret_token: &lt;%= ENV[&#34;PUSHER_LITE_SECRET_TOKEN&#34;] %&gt;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Replace the <code>pusher_host</code>, <code>org_id</code>, <code>app_key</code>, and <code>secret_token</code> for the ones you created before.</p>
<p>Now I want to add a <code>PageController</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;net/http&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;uri&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PageController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s2">&#34;http://</span><span class="si">#{</span><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_host</span><span class="si">}</span><span class="s2">/api/sessions&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">post_form</span><span class="p">(</span><span class="n">uri</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;token&#34;</span> <span class="o">=&gt;</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">secret_token</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@guardian_token</span> <span class="o">=</span> <span class="no">JSON</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">)</span><span class="o">[</span><span class="s2">&#34;jwt&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="no">Rails</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">info</span> <span class="vi">@guardian_token</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>(If you&rsquo;re connecting to my online server, you must use SSL, so change the URL above for &ldquo;https&rdquo;)</p>
<p>What this piece does is submit the secret token in the server-side, to my service, to get a JSON Web Token (JWT) back. Now you can pass this JWT to the front-end to enable authentication.</p>
<p>In the front-end we can have this simple <code>app/views/page/index.html.erb</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>&lt;h1&gt;Ex Pusher Lite - Rails Integration Example&lt;/h1&gt;

&lt;script type=&#34;text/javascript&#34; charset=&#34;utf-8&#34;&gt;
  window.guardian_token = &#34;&lt;%= @guardian_token %&gt;&#34;;
  window.org_id = &#34;&lt;%= Rails.application.secrets.org_id %&gt;&#34;
  window.app_key = &#34;&lt;%= Rails.application.secrets.app_key %&gt;&#34;;
  window.pusher_host = &#34;&lt;%= Rails.application.secrets.pusher_host %&gt;&#34;;
&lt;/script&gt;

&lt;div id=&#34;chat&#34; class=&#34;fixedContainer&#34;&gt;
&lt;/div&gt;

&lt;input type=&#34;text&#34; name=&#34;name&#34; id=&#34;name&#34; value=&#34;&#34; placeholder=&#34;Name&#34;/&gt;
&lt;input type=&#34;text&#34; name=&#34;message&#34; id=&#34;message&#34; value=&#34;&#34; placeholder=&#34;Message&#34;/&gt;
&lt;input type=&#34;checkbox&#34; name=&#34;channel&#34; id=&#34;channel&#34; value=&#34;api&#34;/&gt;
&lt;label for=&#34;channel&#34;&gt;send through API&lt;/label&gt;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Super simple, we can tweak the CSS (<code>app/assets/stylesheets/application.css</code>) just to make it look nicer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">fixedContainer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">height</span><span class="p">:</span> <span class="mi">250</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">padding</span><span class="p">:</span><span class="mi">3</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="kc">black</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">margin</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">overflow</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">body</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">font-family</span><span class="p">:</span> <span class="n">Helvetica</span><span class="p">,</span> <span class="n">Arial</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Finally, we need to load the main Javascript from the ExPusherLite server, so edit the layout at <code>app/views/layouts/application.html.erb</code> and add this line right after the closing <code>&lt;/body&gt;</code> tag:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;http://&lt;%= Rails.application.secrets.pusher_host %&gt;/js/pusher.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And we can now use this Javascript in the <code>app/assets/javascripts/application.js</code> to hook everything up. This is the relevant bit:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">PusherLite</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;pusher_lite&#34;</span><span class="p">).</span><span class="k">default</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">pusher</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PusherLite</span><span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">app_key</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">host</span><span class="o">:</span> <span class="nb">window</span><span class="p">.</span><span class="nx">pusher_host</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">jwt</span><span class="o">:</span> <span class="nb">window</span><span class="p">.</span><span class="nx">guardian_token</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">uniqueUserId</span><span class="o">:</span> <span class="s2">&#34;robot&#34;</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ssl: true - if you&#39;re connecting to my online server
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">publicChannel</span> <span class="o">=</span> <span class="nx">pusher</span><span class="p">.</span><span class="nx">subscribe</span><span class="p">(</span><span class="s2">&#34;general&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">publicChannel</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="s2">&#34;new_message&#34;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">chat</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;#chat&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">chat</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s2">&#34;&lt;p&gt;&lt;strong&gt;&#34;</span> <span class="o">+</span> <span class="nx">payload</span><span class="p">.</span><span class="nx">name</span> <span class="o">+</span> <span class="s2">&#34;&lt;/strong&gt; &#34;</span> <span class="o">+</span> <span class="nx">payload</span><span class="p">.</span><span class="nx">message</span> <span class="o">+</span> <span class="s2">&#34;&lt;/p&gt;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">chat</span><span class="p">.</span><span class="nx">scrollTop</span><span class="p">(</span><span class="nx">chat</span><span class="p">.</span><span class="nx">prop</span><span class="p">(</span><span class="s2">&#34;scrollHeight&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">pusher</span><span class="p">.</span><span class="nx">joinAll</span><span class="p">();</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We can now continue in the same file with the Javascript that binds to the message input field, listening to the &ldquo;Enter&rdquo; key press event to send the messages:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">message_element</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;#message&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">message_element</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;keypress&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">keyCode</span> <span class="o">!=</span> <span class="mi">13</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">name_element</span>    <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;#name&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">check_element</span>   <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;#channel&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">payload</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="nx">name_element</span><span class="p">.</span><span class="nx">val</span><span class="p">(),</span> <span class="nx">message</span><span class="o">:</span> <span class="nx">message_element</span><span class="p">.</span><span class="nx">val</span><span class="p">()</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nx">check_element</span><span class="p">.</span><span class="nx">prop</span><span class="p">(</span><span class="s2">&#34;checked&#34;</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">sendPusher</span><span class="p">(</span><span class="nx">payload</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">sendAPI</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">message_element</span><span class="p">.</span><span class="nx">val</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">window</span><span class="p">.</span><span class="nx">publicChannel</span> <span class="o">=</span> <span class="nx">publicChannel</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is how we send messages to ExPusherLite, either directly through the full-duplex WebSockets:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">sendPusher</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;sending through socket&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nb">window</span><span class="p">.</span><span class="nx">publicChannel</span><span class="p">.</span><span class="nx">trigger</span><span class="p">(</span><span class="s1">&#39;new_message&#39;</span><span class="p">,</span> <span class="nx">payload</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Or Posting to the available API:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>function sendAPI(payload) {
  console.log(&#34;sending through API&#34;)
  $.ajax({
    type : &#39;POST&#39;,
    crossDomain: true,
    url : makeURL(&#34;new_message&#34;),
    headers : { Authorization : &#39;Bearer &#39; &#43; window.guardian_token },
    data : payload,
    success : function(response) {
      console.log(response);
      console.log(&#34;sent through API successfully&#34;);
    },
    error : function(xhr, status, error) {
      console.log(error);
    }
  });
}

function makeURL(event) {
  return &#34;http://&#34; &#43; window.pusher_host &#43; &#34;/api/organizations/&#34; &#43; window.org_id &#43; &#34;/applications/&#34; &#43; window.app_key &#43; &#34;/event/&#34; &#43; event;
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>By the way, you can send messages using the API from the server-side if you want. Specifically from an ActiveJob process so you can keep your Rails web application fast, and you can use the opportunity to store the message in your database, or apply any filters.</p>
<p>And this is it! You now have a Rails application with WebSockets. You can have your lunch and eat it too.</p>
<p>If you want to see this example working, I published a demo app <a href="http://ex-pusher-lite-demo.herokuapp.com/"target="_blank" rel="noopener">over at Heroku</a>. It&rsquo;s just a demo, it has no authentication, no cross-scripting sanitization, no nothing.</p>
<p><a href="http://ex-pusher-lite-demo.herokuapp.com/"target="_blank" rel="noopener"><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/577/Screen_Shot_2016-12-30_at_17.42.22.png" alt="chat demo"  loading="lazy" /></a></p>
<p>In summary: this is a Rails app (you could do it in Django, Laravel, ASP.NET MVC, it doesn&rsquo;t matter) talking through WebSocket + APIs to a Phoenix cluster.</p>
<h3>Next Steps<span class="hx:absolute hx:-mt-20" id="next-steps"></span>
    <a href="#next-steps" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Keep following my blog (or my Twitter at <a href="https://twitter.com/akitaonrails"target="_blank" rel="noopener">@akitaonrails</a> ) for more posts to come.</p>
<p>I am still considering if I will open the ExPusherLite code as open source, so let me know if you&rsquo;re interested.</p>
<p>I am also considering if I will keep the current servers online as a cheap service. You can use it for free right now to play with it, but don&rsquo;t use for production-level apps yet. As I am still heavily coding it, I will keep updating the servers, so there is no SLA. Let me know if you&rsquo;re interested in such a service that keeps the code open source so you can trust it better.</p>
<p>There are important features still missing, such as proper SSL support, encrypted channels, better Presence tracking APIs and so on, but what&rsquo;s available right now already covers most use cases for WebSockets.</p>
<p>And better: because this is Phoenix, because this is Elixir, and because this is Erlang, we get distributed PubSub for &ldquo;free&rdquo;. As I explained in my <a href="http://www.akitaonrails.com/en/2016/12/23/aplicativo-elixir-phoenix-deploy-balanceamento-carga-digitalocean"target="_blank" rel="noopener">deployment</a> post, this is a setup with a server in New York and another in London, just to showcase the distributed nature of Erlang.</p>
<p>It&rsquo;s been very fun to play with Elixir for the past few days and how fast I was able to put together a full-featured solution like this. There were many puzzles that made me scratch my head, figuring out how to deal with cross origin issues, how to make the nodes find each other through the edeliver deployment, figuring out the missing bits in replacing exrm for distillery (which is a transition still taking place in the community), etc.</p>
<p>Now I am quite comfortable with the basics, from bootstrapping a project all the way to deploying in a cluster scenario. And I hope this service proves useful to more people.</p>
<p>As this is possibly my last post of the year: Happy New Year! And I will see you again in 2017!</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>backend</category></item><item><title>Elixir Phoenix App deployed into a Load Balanced DigitalOcean setup</title><link>https://akitaonrails.github.io/en/2016/12/23/elixir-phoenix-app-deployed-into-a-load-balanced-digitalocean-setup/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/12/23/elixir-phoenix-app-deployed-into-a-load-balanced-digitalocean-setup/</guid><pubDate>Fri, 23 Dec 2016 15:07:00 -0200</pubDate><description>&lt;p&gt;One of the main advantages of building a Websockets enabled web application using Phoenix is how &amp;ldquo;easy&amp;rdquo; it is for Erlang to connect itself into a cluster.&lt;/p&gt;
&lt;p&gt;For starters, Erlang does not need multiple processes like Ruby (which is limited to one connection per process, or per thread if you&amp;rsquo;re using a threaded-server like Puma). One single Erlang process will take over the entire machine, if you need to. Internally it will keep one real thread per machine-core. And each thread will have its own Scheduler to manage as many micro-processes as you need. You can read all about it in my post titled &lt;a href="http://www.akitaonrails.com/en/en/2015/11/25/yocto-services-e-meu-primeiro-mes-com-elixir/"target="_blank" rel="noopener"&gt;&amp;ldquo;Yocto Services&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>One of the main advantages of building a Websockets enabled web application using Phoenix is how &ldquo;easy&rdquo; it is for Erlang to connect itself into a cluster.</p>
<p>For starters, Erlang does not need multiple processes like Ruby (which is limited to one connection per process, or per thread if you&rsquo;re using a threaded-server like Puma). One single Erlang process will take over the entire machine, if you need to. Internally it will keep one real thread per machine-core. And each thread will have its own Scheduler to manage as many micro-processes as you need. You can read all about it in my post titled <a href="http://www.akitaonrails.com/en/en/2015/11/25/yocto-services-e-meu-primeiro-mes-com-elixir/"target="_blank" rel="noopener">&ldquo;Yocto Services&rdquo;</a>.</p>
<p>Moreover, Erlang has built-in capabilities to form a cluster, where each Erlang instance acts as a peer-to-peer Node, without the need for a centralized coordinator. You can read all about it in my post about <a href="http://www.akitaonrails.com/en/en/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir/"target="_blank" rel="noopener">Nodes</a>. The power of Erlang is in how &ldquo;easy&rdquo; it is to form reliable distributed systems.</p>
<p>You can fire up many Phoenix instances and from one of the instances, it can broadcast messages to Users subscribed in Channels even if their sockets are connected to different instances. It&rsquo;s seamless and you don&rsquo;t need to do anything special in your code. Phoenix, Elixir and Erlang are doing all the heavy lifting for you behind the scenes.</p>
<h3>No Heroku for You :-(<span class="hx:absolute hx:-mt-20" id="no-heroku-for-you--"></span>
    <a href="#no-heroku-for-you--" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Because you want to take advantage of this scalability and high availability feature for distributed systems (in the small example of a real-time chat system) you will need to have more control over your infrastructure. This requirement rules out the majority of Platform as a Service (PaaS) offerings out there, such as Heroku. Heroku&rsquo;s model revolves around single, volatile processes in isolated containers. Those jailed processes (dynos) are not aware of other processes or the internal networking, so you can&rsquo;t fire up Dynos and have them form a cluster because they won&rsquo;t be able to find each other.</p>
<p>If you already know how to configure Linux related stuff: Postgresql, HAproxy, etc, go ahead directly to the <a href="#phoenix-setup">Phoenix-specific section</a>.</p>
<h3>IaaS (DigitalOcean) to the rescue!<span class="hx:absolute hx:-mt-20" id="iaas-digitalocean-to-the-rescue"></span>
    <a href="#iaas-digitalocean-to-the-rescue" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>You want long lived processes in network reachable servers (either through private networking, VPN, or plain simple - insecure! - public networks).</p>
<p>In this example I want to walk you through a very simple deployment using DigitalOcean (you can choose any IaaS, such as AWS, Google Cloud, Azure or whatever you feel more comfortable with).</p>
<p>I have created 4 droplets (all using the smallest size of 512Mb of RAM):</p>
<ul>
<li>1 Postgresql database (single point of failure: it&rsquo;s not the focus of this article to build a highly available, replicated database setup);</li>
<li>1 HAProxy server (single point of failure: again, it&rsquo;s not the focus to create a highly available load balancing scheme);</li>
<li>2 Phoenix servers - one in the NYC datacenter and another in the London datacenter, to demonstrate how easy it is for Erlang to form clusters even with geographically separated boxes.</li>
</ul>
<h3>Basic Ubuntu 16.04 configuration<span class="hx:absolute hx:-mt-20" id="basic-ubuntu-1604-configuration"></span>
    <a href="#basic-ubuntu-1604-configuration" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>Goals: configure locale, assure unattended updates are up, upgrade packages, install and configure Elixir and Node.</li>
<li>You should also do: change SSH to another port and install [fail2ban](<a href="https://www.digitalocean.com/community/tutorials/how-to-protect-ssh-with-fail2ban-on-ubuntu-14-04"target="_blank" rel="noopener">https://www.digitalocean.com/community/tutorials/how-to-protect-ssh-with-fail2ban-on-ubuntu-14-04</a>, disallow login through password).</li>
</ul>
<p>You will want to read my post about <a href="http://www.akitaonrails.com/en/2016/09/21/ubuntu-16-04-lts-xenial-no-vagrant-com-vmware-fusion"target="_blank" rel="noopener">configuring Ubuntu 16.04</a>. To summarize, start by configuring proper UTF-8:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo locale-gen &#34;en_US.UTF-8&#34;
sudo dpkg-reconfigure locales
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
echo &#39;LC_ALL=en_US.UTF-8&#39; | sudo tee -a /etc/environment
echo &#39;LANG=en_US.UTF-8&#39; | sudo tee -a /etc/environment</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Make sure you add a proper user into the sudo group and from now on do not use the root user. I will create a user named <code>pusher</code> and I will explain in another post why. You should create a username that suits your application.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>adduser pusher
usermod -aG sudo pusher</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now log out and log in again through this user. <code>ssh pusher@server-ip-address</code>. If you&rsquo;re on a Mac copy the public key of your SSH certificate like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ssh-copy-id -i ~/.ssh/id_ed25519.pub pusher@server-ip-address</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It creates the <code>.ssh/authorized_keys</code> if it doesn&rsquo;t exist, sets the correct permission bits and appends your public key. You can do it manually, of course.</p>
<p>DigitalOcean&rsquo;s droplets start without a swap file and I&rsquo;d recommend adding one, especially if you want to start with the smaller boxes with less than 1GB of RAM:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo cp /etc/fstab /etc/fstab.bak
echo &#39;/swapfile none swap sw 0 0&#39; | sudo tee -a /etc/fstab
sudo sysctl vm.swappiness=10
sudo sysctl vm.vfs_cache_pressure=50
echo &#39;vm.swappiness=10&#39; | sudo tee -a /etc/sysctl.conf
echo &#39;vm.vfs_cache_pressure=50&#39; | sudo tee -a /etc/sysctl.conf</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Make sure you have unattended upgrades configured. You will want at least to have security updates automatically installed when available.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt install unattended-upgrades</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, let&rsquo;s install Elixir and Node (Phoenix needs Node.js):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb &amp;&amp; sudo dpkg -i erlang-solutions_1.0_all.deb
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install build-essential nodejs esl-erlang elixir erlang-eunit erlang-base-hipe
sudo npm install -g brunch
mix local.hex
mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez # this is optional, install if you want to manually test phoenix in your box</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now you have an Elixir capable machine ready. Create a image snapshot over DigitalOcean, move it to the regions where you want to create your other droplets and use this image to create as many droplets as you need.</p>
<p>For this example, I created a second droplet in the London region, a third droplet for postgresql in the NYC1 region and a fourth droplet in the NYC3 region for HAProxy.</p>
<p>I will refer to their public IP addresses as <strong>&ldquo;nyc-ip-address&rdquo;</strong>, <strong>&ldquo;lon-ip-address&rdquo;</strong>, <strong>&ldquo;pg-ip-address&rdquo;</strong>, and <strong>&ldquo;ha-ip-address&rdquo;</strong>.</p>
<h3>Basic PostgreSQL configuration<span class="hx:absolute hx:-mt-20" id="basic-postgresql-configuration"></span>
    <a href="#basic-postgresql-configuration" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>Goal: basic configuration of Postgresql to allow the Phoenix servers to connect.</li>
<li>To do: create a secondary role just to connect to the application database and another superuser role to create the database and migrate the schema. Also lock down the machine and configure SSH tunnels or another secure way, at least private networking, rather than allowing plain 5432 TCP port connections from the public Internet.</li>
</ul>
<p>Now you can connect to <code>ssh pusher@pg-ip-address</code> and follow this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install postgresql postgresql-contrib</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You should create a new role with the same name of the user you added above (&ldquo;pusher&rdquo; in our example):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ sudo -u postgres createuser --interactive

Enter name of role to add: pusher
Shall the new role be a superuser? (y/n) y

$ sudo -u postgres createdb pusher</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Postgresql expects to find a database with the same name as the role and the role should have the same name as the Linux user. Now you can use <code>psql</code> to set a password for this new role:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ sudo -u postgres psql
\password pusher</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Register a secure password, take note and let&rsquo;s move on.</p>
<p>Postgresql comes locked down to external connections. One way to connect from the outside is to configure your servers to create an <a href="https://www.postgresql.org/docs/9.1/static/ssh-tunnels.html"target="_blank" rel="noopener">SSH tunnel</a> to the database server and keep external TCP connections through port 5432 disavowed.</p>
<p>But for this example, we will just allow connections from the public Internet to the 5432 TCP port. <strong>Warning:</strong> this is VERY insecure!</p>
<p>Edit the <code>/etc/postgresql/9.5/main/postgresql.conf</code> and find the <code>listen_addresses</code> configuration line and allow it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>listen_addresses = &#39;*&#39;    # what IP address(es) to listen on;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This should bind the server to the TCP port. Now edit <code>/etc/postgresql/9.5/main/pg_hba.conf</code> and edit the end to look like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># IPv4 local connections:
host    all             all             127.0.0.1/32            trust
host    all             all             your-local-machine-ip-address/24        trust
host    all             all             nyc-ip-address/24       trust
host    all             all             lon-ip-address/24       trust
# IPv6 local connections:
host    all             all             ::1/128                 trust</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Save the configuration file and restart the server:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo service postgresql restart</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>See what I did there? I only allowed connections coming from the public IPs of the Phoenix servers. This does not make the server secure, just a little bit less vulnerable. If you&rsquo;re behind a DHCP/NAT based network, just Google for &ldquo;what&rsquo;s my IP&rdquo; to see your public facing IP address - which is probably shared by many other users, remember you&rsquo;re allowing connections from an insecure IP to your database server! Once you make initial tests, create your new schema, then you should remove that <code>your-local-machine-ip-address/24</code> line from the configuration.</p>
<p>From your Phoenix application, you can edit your local <code>config/prod.secret.exs</code> file to look like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># Configure your database</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:your_app_name</span><span class="p">,</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Repo</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">adapter</span><span class="p">:</span> <span class="no">Ecto</span><span class="o">.</span><span class="n">Adapters</span><span class="o">.</span><span class="n">Postgres</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">username</span><span class="p">:</span> <span class="s2">&#34;pusher&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">password</span><span class="p">:</span> <span class="s2">&#34;your-super-secure-pg-password&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">database</span><span class="p">:</span> <span class="s2">&#34;your-app-database-name&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">hostname</span><span class="p">:</span> <span class="s2">&#34;pg-ip-address&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">pool_size</span><span class="p">:</span> <span class="mi">20</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Replace the information for your server and database and now you can test it out like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>MIX_ENV=prod iex -S mix phoenix.server</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If you see a <code>:econnrefused</code> message from postgrex, then you&rsquo;re in trouble. Re-check your configuration, restart the server and try again. If everything connects, you can run <code>MIX_ENV=prod mix do ecto.create, ecto.migrate</code> to prepare your database.</p>
<p>Finally, you will want to lock down the rest of your server with UFW, at the very least. UFW should come pre-installed in Ubuntu 16, so you can just do:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ufw allow 5432
ufw allow ssh
ufw enable</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>That&rsquo;s it. And again, this does not make your server secure, it just makes it less insecure. There is a huge difference!</p>
<p>And by the way, if you&rsquo;re a Docker fan:</p>
<blockquote>
  <p>DO NOT INSTALL A DATABASE INSIDE A DOCKER CONTAINER!</p>

</blockquote>
<p><a href="http://patrobinson.github.io/2016/11/07/thou-shalt-not-run-a-database-inside-a-container/"target="_blank" rel="noopener">You have been warned</a>!</p>
<h3>Basic HAProxy configuration<span class="hx:absolute hx:-mt-20" id="basic-haproxy-configuration"></span>
    <a href="#basic-haproxy-configuration" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>Goals: Provide a simple solution to load balance between our 2 Phoenix servers.</li>
<li>To do: There is something wrong with session checking or something like that as sometimes I have to refresh my browser so I am not sent back to the login form in my application. Phoenix uses cookie-based sessions so I don&rsquo;t think it is missing sessions.</li>
</ul>
<p>Now let&rsquo;s <code>ssh pusher@ha-ip-address</code>. This one is easy, let&rsquo;s just install HAProxy:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install haproxy</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Edit <code>/etc/haproxy/haproxy.cfg</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>...
listen your-app-name
  bind 0.0.0.0:80
  mode http
  stats enable
  stats uri /haproxy?stats
  stats realm Strictly\ Private
  stats auth admin:some-secure-password-for-admin
  option forwardfor
  option http-server-close
  option httpclose
  balance roundrobin
  cookie SERVERID insert indirect nocache
  server us your_us_server_IP:8080 check cookie us1
  server uk your_uk_server_IP:8080 check cookie uk1</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You can avoid the <code>stats</code> lines if you have other means of monitoring, otherwise set a secure password for the <code>admin</code> user. One very important part is the <code>option http-server-close</code> as explained in <a href="http://blog.silverbucket.net/post/31927044856/3-ways-to-configure-haproxy-for-websockets"target="_blank" rel="noopener">this other blog post</a>, otherwise you may have trouble with Websockets.</p>
<p>For some reason I am having some trouble with my application after I login and it sets the session, sometimes I have to refresh to be sent to the correct page, not sure why yet and I believe it&rsquo;s something in the HAProxy configuration. If anyone knows what it is, let me know in the comments section below. For now, I am just relying on Sticky sessions (server affinity) by making HAProxy write back a cookie with the server.</p>
<p>Now you can restart the server and enable UFW as well:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo service haproxy restart
sudo ufw allow http
sudo ufw allow https
sudo ufw allow ssh
sudo ufw enable</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You can easily add SSL support to your application by configuring HAProxy (and not the Phoenix nodes). The <a href="https://www.digitalocean.com/community/tutorials/how-to-secure-haproxy-with-let-s-encrypt-on-ubuntu-14-04"target="_blank" rel="noopener">DigitalOcean documentation</a> is comprehensive, so just follow it. At the end, my <code>haproxy.cfg</code> looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>global
   log /dev/log    local0
   log /dev/log    local1 notice
   chroot /var/lib/haproxy
   stats socket /run/haproxy/admin.sock mode 660 level admin
   stats timeout 30s
   user haproxy
   group haproxy
   daemon
   maxconn 2048
   tune.ssl.default-dh-param 2048

defaults
   log global
   mode http
   option httplog
   option dontlognull
   option redispatch
   option forwardfor
   option http-server-close
   timeout connect 5000
   timeout client  50000
   timeout server  50000

frontend www-http
   bind your_ha_proxy_IP:80
   reqadd X-Forwarded-Proto:\ http
   default_backend www-backend

frontend www-https
   bind your_ha_proxy_IP:443 ssl crt /etc/haproxy/certs/your_domain.pem
   reqadd X-Forwarded-Proto:\ https
   acl letsencrypt-acl path_beg /.well-known/acme-challenge/
   use_backend letsencrypt-backend if letsencrypt-acl
   default_backend www-backend

backend www-backend
   redirect scheme https if !{ ssl_fc }
   # setting session stickiness
   cookie SERVERID insert indirect nocache
   server us your_us_server_IP:8080 check cookie us1
   server uk your_uk_server_IP:8080 check cookie uk1

backend letsencrypt-backend
   server letsencrypt 127.0.0.1:54321</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Finally, I will assume you have a DNS server/service somewhere where you can register the IP of this HAproxy server as an A record so you can access it by a full name such as &ldquo;your-app-name.mydomain.com&rdquo;.</p>
<p><a name="phoenix-setup"></a></p>
<h3>Basic Phoenix Configuration<span class="hx:absolute hx:-mt-20" id="basic-phoenix-configuration"></span>
    <a href="#basic-phoenix-configuration" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>Goal: configure the Phoenix app to be deployable. Configure the servers to have the necessary configuration files.</li>
<li>To do: find out a way to cut down the super slow deployment times.</li>
</ul>
<p>Finally, we have almost everything in place.</p>
<p>I will assume that you have a working Phoenix application already in place, otherwise create one from the many tutorials out there.</p>
<p>I have assembled this information from <a href="https://medium.com/@diamondgfx/deploying-phoenix-applications-with-exrm-97a3867ebd04#.7qyuplncx"target="_blank" rel="noopener">posts</a> such as <a href="http://engineering.pivotal.io/post/how-to-set-up-an-elixir-cluster-on-amazon-ec2/"target="_blank" rel="noopener">this very helpful one</a> from Pivotal about an AWS-based deployment. In summary you must do a number of changes to your configuration.</p>
<p>When you&rsquo;re developing your application, you will notice that whenever you run it, it delta-compiles what changed. The binary bits are in the <code>_build/dev</code> or <code>_build/test</code> in the form of <code>.beam</code> binaries (similar to what <code>.class</code> are for Java).</p>
<p>Different from Ruby or Python or PHP, you are not deploying source-code to production servers. It&rsquo;s more akin to Java, where you must have everything compiled into binary bits and packaged into what&rsquo;s called a <strong>release</strong>. It&rsquo;s like a &ldquo;.war&rdquo; or &ldquo;.ear&rdquo; if you&rsquo;re from Java.</p>
<p>To create this package people usually use &ldquo;exrm&rdquo;, but it&rsquo;s being replaced by <a href="https://github.com/bitwalker/distillery"target="_blank" rel="noopener">&ldquo;distillery&rdquo;</a>, so we will use it.</p>
<p>Then, if you&rsquo;re from Ruby you&rsquo;re familiar with Capistrano. Or if you&rsquo;re from Python, you know Capistrano&rsquo;s clone, Fabric. Elixir has a similar tool (much simpler at this point), called <a href="https://github.com/boldpoker/edeliver"target="_blank" rel="noopener">&ldquo;edeliver&rdquo;</a>. It&rsquo;s your basic SSH automation tool.</p>
<p>You add them to <code>mix.exs</code> just like any other dependency:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">application</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">[</span><span class="ss">mod</span><span class="p">:</span> <span class="p">{</span><span class="no">ExPusherLite</span><span class="p">,</span> <span class="o">[]</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">   <span class="ss">applications</span><span class="p">:</span> <span class="o">[...</span><span class="p">,</span> <span class="ss">:edeliver</span><span class="o">]]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">deps</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">[...</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="ss">:edeliver</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.4.0&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="ss">:distillery</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.0&#34;</span><span class="p">}</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>From the Pivotal blog post, the important thing to not forget is to edit this part in the <code>config/prod.exs</code> file:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="ss">http</span><span class="p">:</span> <span class="o">[</span><span class="ss">port</span><span class="p">:</span> <span class="mi">8080</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="ss">url</span><span class="p">:</span> <span class="o">[</span><span class="ss">host</span><span class="p">:</span> <span class="s2">&#34;your-app-name.yourdomain.com&#34;</span><span class="p">,</span> <span class="ss">port</span><span class="p">:</span> <span class="mi">80</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:phoenix</span><span class="p">,</span> <span class="ss">:serve_endpoints</span><span class="p">,</span> <span class="kp">true</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You MUST hardcode the default PORT of the Phoenix web server and the allowed domain (remember the domain name you associated to your HAProxy server above? That one). And you MUST uncomment the <code>:serve_endpoints, true</code> line!</p>
<p>For edeliver to work you have to create a <code>.deliver/config</code> file like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>USING_DISTILLERY=true

# change this to your app name:
APP=&#34;your-app-name&#34;

# change this to your own servers IP and add as many as you want
US=&#34;nyc-ip-address&#34;
UK=&#34;lon-ip-address&#34;

# the user you created in your Ubuntu machines above
USER=&#34;pusher&#34;

# which server do you want to build the first release?
BUILD_HOST=$US
BUILD_USER=$USER
BUILD_AT=&#34;/tmp/edeliver/$APP/builds&#34;

# list the production servers declared above:
PRODUCTION_HOSTS=&#34;$US $UK&#34;
PRODUCTION_USER=$USER
DELIVER_TO=&#34;/home/$USER&#34;

# do not change here

LINK_VM_ARGS=&#34;/home/$USER/vm.args&#34;

# For *Phoenix* projects, symlink prod.secret.exs to our tmp source
pre_erlang_get_and_update_deps() {
  local _prod_secret_path=&#34;/home/$USER/prod.secret.exs&#34;
  if [ &#34;$TARGET_MIX_ENV&#34; = &#34;prod&#34; ]; then
    __sync_remote &#34;
      ln -sfn &#39;$_prod_secret_path&#39; &#39;$BUILD_AT/config/prod.secret.exs&#39;

      cd &#39;$BUILD_AT&#39;

      mkdir -p priv/static

      mix deps.get

      npm install

      brunch build --production

      APP=&#39;$APP&#39; MIX_ENV=&#39;$TARGET_MIX_ENV&#39; $MIX_CMD phoenix.digest $SILENCE
    &#34;
  fi
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Remember the information we&rsquo;ve been gathering since the beginning of this long recipe? These are the options you MUST change to your own. Just follow the comments in the file content above and add it to your git repository. By the way, your project is in a proper GIT repository, RIGHT??</p>
<p>If you like to use passphrase protected SSH private keys, then it&rsquo;s going to be a huge pain to deploy because for each command, edeliver will issue an SSH command that will keep asking for you passphrase, a dozen times through everything. You&rsquo;ve been warned! If you still don&rsquo;t mind that, and you&rsquo;re in a Mac you will have an extra trouble because the Terminal will not be able to create a prompt for you to input your passphrase. You must create an <code>/usr/local/bin/ssh-askpass</code> <a href="https://github.com/markcarver/mac-ssh-askpass"target="_blank" rel="noopener">script</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>#!/bin/bash
# Script: ssh-askpass
# Author: Mark Carver
# Created: 2011-09-14
# Licensed under GPL 3.0

# A ssh-askpass command for Mac OS X
# Based from author: Joseph Mocker, Sun Microsystems
# http://blogs.oracle.com/mock/entry/and_now_chicken_of_the

# To use this script:
#   Install this script running INSTALL as root
#
# If you plan on manually installing this script, please note that you will have
# to set the following variable for SSH to recognize where the script is located:
#   export SSH_ASKPASS=&#34;/path/to/ssh-askpass&#34;

TITLE=&#34;${SSH_ASKPASS_TITLE:-SSH}&#34;;
TEXT=&#34;$(whoami)&#39;s password:&#34;;
IFS=$(printf &#34;\n&#34;);
CODE=(&#34;on GetCurrentApp()&#34;);
CODE=(${CODE[*]} &#34;tell application \&#34;System Events\&#34; to get short name of first process whose frontmost is true&#34;);
CODE=(${CODE[*]} &#34;end GetCurrentApp&#34;);
CODE=(${CODE[*]} &#34;tell application GetCurrentApp()&#34;);
CODE=(${CODE[*]} &#34;activate&#34;);
CODE=(${CODE[*]} &#34;display dialog \&#34;${@:-$TEXT}\&#34; default answer \&#34;\&#34; with title \&#34;${TITLE}\&#34; with icon caution with hidden answer&#34;);
CODE=(${CODE[*]} &#34;text returned of result&#34;);
CODE=(${CODE[*]} &#34;end tell&#34;);
SCRIPT=&#34;/usr/bin/osascript&#34;
for LINE in ${CODE[*]}; do
      SCRIPT=&#34;${SCRIPT} -e $(printf &#34;%q&#34; &#34;${LINE}&#34;)&#34;;
done;
eval &#34;${SCRIPT}&#34;;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now do this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo chmod &#43;x /usr/local/bin/ssh-askpass
sudo ln -s /usr/local/bin/ssh-askpass /usr/X11R6/bin/ssh-askpass</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Remember, Macs only. And now every time you try to deploy you will receive a number of graphical prompt windows asking for the SSH private key passphrase. It&rsquo;s freaking annoying! And you must have <a href="https://www.xquartz.org/"target="_blank" rel="noopener">XQuartz</a> installed, by the way.</p>
<p>Now you must manually create 3 files in all Phoenix servers. Start with the <code>your-app-name/vm.args</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>-name us@nyc-ip-address
-setcookie @bCd&amp;fG
-kernel inet_dist_listen_min 9100 inet_dist_listen_max 9155
-config /home/pusher/your-app-name.config
-smp auto</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <code>/home/pusher/your-app-name</code> is the directory where the release will be uncompressed after edeliver deploys the release tarball.</p>
<p>You must create this file in all Phoenix machines, by the way, changing the <code>-name</code> bit for the same name you declared in the <code>.deliver/config</code> file. The <code>-setcookie</code> should be any name, as long as it&rsquo;s the same in all servers.</p>
<p>See the <code>-config /home/pusher/your-app-name.config</code>? Create that file with the following:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[{kernel,
  [
    {sync_nodes_optional, [&#39;uk@lon-ip-address&#39;]},
    {sync_nodes_timeout, 30000}
  ]}
].</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is an Erlang source-code. On the NYC machine you must declare the London name, and vice-versa. If you have several machines, all of them but the one you&rsquo;re in right now. Get it?</p>
<p>Finally, for the Phoenix app itself, you always have a <code>config/prod.secret.exs</code> that should never be <code>git add</code>ed to the repository, remember? This is where you put the Postgresql server information and random secret key to sign the session cookies:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>use Mix.Config

config :your_app_name, YourAppName.Endpoint,
  secret_key_base: &#34;...&#34;

# Configure your database
config :your_app_name, YourAppName.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: &#34;pusher&#34;,
  password: &#34;your-super-secure-pg-password&#34;,
  database: &#34;your-app-database-name&#34;,
  hostname: &#34;pg-ip-address&#34;,
  pool_size: 20

# if you have Guardian, for example:
config :guardian, Guardian,
  secret_key: &#34;...&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>How do you create a new random secret key? From your development machine just run: <code>mix phoenix.gen.secret</code> and copy the generated string into the file above.</p>
<p>So now you must have those 3 files in each Phoenix server, in the <code>/home/pusher</code> home folder:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>~/your-app-name/vm.args
~/prod.secret.exs
~/your-app-name.config</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As per <a href="https://hexdocs.pm/distillery/runtime-configuration.html#vm-args"target="_blank" rel="noopener">distillery</a> documentation, you have to set an environment variable to let it know about the <code>vm.args</code> file, and this is <strong>SUPER</strong> important otherwise it will generate a default one that doesn&rsquo;t set the proper name and cookie, so the nodes will not find each other after booting up.</p>
<p>Using <code>sudo</code>, edit the <code>/etc/environment</code> and add the line:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>RELEASE_CONFIG_DIR=/home/pusher/your-app-name
VMARGS_PATH=/home/pusher/your-app-name/vm.args</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You have to do it in all the phoenix servers.</p>
<p>In your local development directory you still have to run this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix release.init</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It will generate a standard <code>rel/config.exs</code> file that you must add to your git repository with the following changes to the bottom:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">environment</span> <span class="ss">:prod</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">plugin</span> <span class="no">Releases</span><span class="o">.</span><span class="n">Plugin</span><span class="o">.</span><span class="n">LinkConfig</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">release</span> <span class="ss">:your_app_name</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">set</span> <span class="ss">version</span><span class="p">:</span> <span class="n">current_version</span><span class="p">(</span><span class="ss">:your_app_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">set</span> <span class="ss">applications</span><span class="p">:</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">    <span class="ss">your_app_name</span><span class="p">:</span> <span class="ss">:permanent</span>
</span></span><span class="line"><span class="cl">  <span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The plugin should enable the release to find the local <code>vm.args</code> file in the server (this is super important, otherwise the remote commands such as start, stop, ping, etc will not work and the nodes will not load the correct information to boot up and form a cluster).</p>
<p>Finally, all set, you can issue this command:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ mix edeliver build release production --skip-mix-clean --verbose</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If it finishes without errors you will have a message like the following in the end:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>...
==&gt; Release successfully built!
    You can run it in one of the following ways:
      Interactive: _build/prod/rel/your-app-name/bin/your-app-name console
      Foreground: _build/prod/rel/your-app-name/bin/your-app-name foreground
      Daemon: _build/prod/rel/your-app-name/bin/your-app-name start
--&gt; Copying release 0.0.1 to local release store
--&gt; Copying your-app-name.tar.gz to release store

RELEASE BUILD OF YOUR-APP-NAME WAS SUCCESSFUL!</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, this will take an absurdly long time to deploy. That&rsquo;s because it will git clone the source code of your app, fetch all Elixir dependencies (every time!), it will have to compile everything, then it will run the super slow <code>npm install</code> (every time!), brunch your assets, create the so-called &ldquo;release&rdquo;, tar and gzip it, download it and SCP it to the other machines you configured.</p>
<p>In the <code>.deliver/config</code> file you set a <code>BUILD_HOST</code> option. This is the machine where all this process takes place, so you will want to have at least this machine be beefier than the others. As I am using small 512Mb droplets, the process takes forever.</p>
<p>The last command will download the generated tarball. It has to do it to make sure you have the <a href="http://erlang.org/doc/tutorial/nif.html"target="_blank" rel="noopener">NIFs</a> compiled in the native environment where they run, because if you use a Mac, binaries from Macs won&rsquo;t run on Linux.</p>
<p>Now we must upload and decompress this tarball in each server with the following command:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix edeliver deploy release to production</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The slower your network, the longer this will take, as it&rsquo;s uploading a big tarfile across the public internet, so make sure you&rsquo;re in a fast connection. And when it finishes, we can restart the servers (if you already had instances running):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix edeliver stop production
mix edeliver start production</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If you do everything right, the edeliver process finishes without any error and it leaves a daemon running in your server, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>/home/pusher/your-app-name/erts-8.2/bin/beam -- -root /home/pusher/your-app-name -progname home/pusher/your-app-name/releases/0.0.1/your-app-name.sh -- -home /home/pusher -- -boot /home/pusher/your-app-name/releases/0.0.1/your-app-name -config /home/pusher/your-app-name/running-config/sys.config -boot_var ERTS_LIB_DIR /home/pusher/your-app-name/erts-8.2/../lib -pa /home/pusher/your-app-name/lib/your-app-name-0.0.1/consolidated -name us@nyc-ip-address -setcookie ex-push&amp;r-l!te -kernel inet_dist_listen_min 9100 inet_dist_listen_max 9155 -config /home/pusher/your-app-name.config -mode embedded -user Elixir.IEx.CLI -extra --no-halt &#43;iex -- console</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It will still take a long time, but it should be easier. So this is a pro-tip for you, Linux users. Follow <a href="https://gist.github.com/mattweldon/2e8ecb953216438ad168"target="_blank" rel="noopener">this Gist</a> for more details, you must emulate what&rsquo;s run from the <code>.deliver/config</code> file&rsquo;s bottom half.</p>
<p>Also notice that I ran the migrations manually, but you can do it using <code>mix edeliver migrate</code>.</p>
<p>Read their documentation for more commands and configurations.</p>
<p>Also, do not forget to enable UFW:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo ufw allow ssh
sudo ufw allow 8080
sudo ufw allow 4369
sudo ufw allow proto tcp from any to any port 9100:9155
sudo ufw default allow outgoing
sudo ufw enable</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Debugging Production bugs<span class="hx:absolute hx:-mt-20" id="debugging-production-bugs"></span>
    <a href="#debugging-production-bugs" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Right after I deployed, obviously it failed. And the problem is that the <code>/home/pusher/your-app-name/log/erlang.log</code> files (they are automatically rotated so you may find several files ending in a number), you won&rsquo;t see much.</p>
<p>What I recommend you to do is to change the <code>config/prod.exs</code> file ONLY in your development machine and change the log setting to <code>config :logger, level: :debug</code>, use the same <code>prod.secret.exs</code> you edited in the servers above and run it locally with <code>MIX_ENV=prod iex -S mix phoenix.server</code>.</p>
<p>For example, in development mode I had a code in the controller that was checking the existence of an optional query string parameter like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">params</span><span class="o">[</span><span class="s2">&#34;some_parameter&#34;</span><span class="o">]</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"> <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>That was working fine in development but crashing in production, so I had to change it to:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">if</span> <span class="no">Map</span><span class="o">.</span><span class="n">has_key?</span><span class="p">(</span><span class="n">params</span><span class="p">,</span> <span class="s2">&#34;some_parameter&#34;</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"> <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Another thing was that Guardian was working normally in development, but in production I had to declare its application in the <code>mix.exs</code> like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">application</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">[</span><span class="ss">mod</span><span class="p">:</span> <span class="p">{</span><span class="no">ExPusherLite</span><span class="p">,</span> <span class="o">[]</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">   <span class="ss">applications</span><span class="p">:</span> <span class="o">[...</span><span class="p">,</span> <span class="ss">:guardian</span><span class="p">,</span> <span class="ss">:edeliver</span><span class="o">]]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I was getting <code>:econnrefused</code> errors because I forgot to run <code>MIX_ENV=prod mix do ecto.create, ecto.migrate</code> as I instructed above. Once I figured those out, my application was up and running through the <code>http://your-app-name.yourdomain.com</code>, HAProxy was correctly forwarding to the 8080 port in the servers and everything runs fine, including the WebSocket connections.</p>
<h3>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>As I mentioned above, this kind of procedure makes me really miss an easy to deploy solution such as Heroku.</p>
<p>The only problem I am facing right now is that when I log in through Coherence&rsquo;s sign in page, I am not redirected to the correct URI I am trying (&quot;/admin&quot; in my case), sometimes reloading after sign in works, sometimes it doesn&rsquo;t. Sometimes I am inside a &ldquo;/admin&rdquo; page but when I click one of the links it sends me back to the sign in page even though I am already signed in. I am not sure if it&rsquo;s a bug in Coherence, ExAdmin, Phoenix itself or an HAProxy misconfiguration. I will update this post if I find out.</p>
<p>Edeliver also takes an obscene amount of time to deploy. Even waiting for sprockets to process in a <code>git push heroku master</code> deploy feels way faster in comparison. And this is for a very bare-bone Phoenix app. Having to fetch everything (because Hex doesn&rsquo;t keep a local global cache, all dependencies are statically vendored in the project directory). Having to run the super slow npm doesn&rsquo;t help either.</p>
<p>I still need to research if there are faster options, but for now what I have &ldquo;works&rdquo;.</p>
<p>And more importantly, now I have a scalable cluster for real-time bi-directional WebSockets, which is the main reason one might want to use Phoenix in the first place.</p>
<p>If you want to build a &ldquo;normal&rdquo; website, keep it simple and do it in Rails, Django, Express or whatever is your web framework of choice. If you want real-time communications the easy way, I might have a better solution. Keep an eye on my blog for news to come soon! ;-)</p>
]]></content:encoded><category>elixir-and-erlang</category><category>devops</category><category>cloud</category><category>distributed-systems</category></item><item><title>Coherence and ExAdmin - Devise and ActiveAdmin for Phoenix</title><link>https://akitaonrails.github.io/en/2016/12/06/coherence-and-exadmin-devise-and-activeadmin-for-phoenix/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/12/06/coherence-and-exadmin-devise-and-activeadmin-for-phoenix/</guid><pubDate>Tue, 06 Dec 2016 18:18:00 -0200</pubDate><description>&lt;p&gt;This is intended for Rubyists researching the possibility of replacing some of Ruby and Rails with Elixir and Phoenix.&lt;/p&gt;
&lt;p&gt;Cutting straight to the chase, many small Rails apps that I build start with 2 very simple add-ons: Devise for authentication and ActiveAdmin for basic database management. Then I build up from there.&lt;/p&gt;
&lt;p&gt;Both Elixir and Phoenix are fast moving targets right now, which makes it difficult for a stable set of libraries to solidify properly, but I think that we&amp;rsquo;re finally getting past the early adopters curve.&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is intended for Rubyists researching the possibility of replacing some of Ruby and Rails with Elixir and Phoenix.</p>
<p>Cutting straight to the chase, many small Rails apps that I build start with 2 very simple add-ons: Devise for authentication and ActiveAdmin for basic database management. Then I build up from there.</p>
<p>Both Elixir and Phoenix are fast moving targets right now, which makes it difficult for a stable set of libraries to solidify properly, but I think that we&rsquo;re finally getting past the early adopters curve.</p>
<p>One big point of contention has been user authentication. Many purists will argue that you need to build your own from scratch or by using low level libraries such as <a href="https://github.com/ueberauth/guardian"target="_blank" rel="noopener">Guardian</a>.</p>
<p>If you&rsquo;re building an application that just exposes API endpoints, that&rsquo;s probably fine. But for a full-featured web app meant for humans to use, this is hardly a good choice. I will not entertain the discussion today, as it&rsquo;s beyond the point.</p>
<p>I am assuming that you at least followed both <a href="http://elixir-lang.org/getting-started/introduction.html"target="_blank" rel="noopener">Elixir</a> and <a href="http://www.phoenixframework.org/docs/installation"target="_blank" rel="noopener">Phoenix</a> tutorials by now. If you didn&rsquo;t go ahead and do it, it will take you one or two days to learn the very basics if you&rsquo;re already an experienced rubyist. Then come back and read <a href="/elixir">my posts on Elixir</a> to understand where it stands out compared to everything else.</p>
<p>That being said, let&rsquo;s get started.</p>
<h3>Coherence (Devise alternative)<span class="hx:absolute hx:-mt-20" id="coherence-devise-alternative"></span>
    <a href="#coherence-devise-alternative" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Finally, I found this project that&rsquo;s been under heavy development for the past 6 months called <a href="https://github.com/smpallen99/coherence"target="_blank" rel="noopener">Coherence</a>. For all intents and purposes, it successfully mimics Devise in almost every way. And this is a very good thing for a lot of scenarios.</p>
<p>Their <a href="https://github.com/smpallen99/coherence/blob/master/README.md"target="_blank" rel="noopener">README</a> is well explained enough so I will not copy and paste it here, just read it there to get up and running. But if you want to try out all of their features you can tweak their procedure with this set of options in the installation Mix task:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix coherence.install --full --rememberable --invitable --trackable</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Run <code>mix help coherence.install</code> to see a description for all the options.</p>
<p>And if you&rsquo;re not tweaking the front-end, you can just add the proper sign up, sign in, sign out links by adding the following snippet to the <code>web/templates/layout/app.html.eex</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">header</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;header&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">nav</span> <span class="na">role</span><span class="o">=</span><span class="s">&#34;navigation&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">ul</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;nav nav-pills pull-right&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="err">&lt;</span>%= if Coherence.current_user(@conn) do %&gt;
</span></span><span class="line"><span class="cl">        <span class="err">&lt;</span>%= if @conn.assigns[:remembered] do %&gt;
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">li</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;color: red;&#34;</span><span class="p">&gt;</span>!!<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;</span>% end %&gt;
</span></span><span class="line"><span class="cl">      <span class="err">&lt;</span>% end %&gt;
</span></span><span class="line"><span class="cl">      <span class="err">&lt;</span>%= YourApp.Coherence.ViewHelpers.coherence_links(@conn, :layout) %&gt;
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;http://www.phoenixframework.org/docs&#34;</span><span class="p">&gt;</span>Get Started<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">nav</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">header</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">...</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>(By the way, whenever you see <code>YourApp</code> in the code snippets, you must change it to your app&rsquo;s module name.)</p>
<p>If you get lost in their documentation you can check out their <a href="https://github.com/smpallen99/coherence_demo"target="_blank" rel="noopener">Coherence Demo</a> repository for an example of a basic Phoenix app with Coherence already configured and working. You will mostly have to take care of <code>web/router.ex</code> to create a <code>:protected</code> pipeline and set the scopes accordingly.</p>
<p>If you do it correctly, this is what you will see:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/572/Screen_Shot_2016-12-06_at_17.45.47.png" alt="Coherence Navigation Links"  loading="lazy" /></p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/573/Screen_Shot_2016-12-06_at_17.45.52.png" alt="Coherence Sign In Form"  loading="lazy" /></p>
<p>It&rsquo;s been a long while since I got excited by a simple sign in page!</p>
<h3>Ex Admin (ActiveAdmin alternative)<span class="hx:absolute hx:-mt-20" id="ex-admin-activeadmin-alternative"></span>
    <a href="#ex-admin-activeadmin-alternative" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Then, the next step I usually like to do is to add a simple Administration interface. To that end I found the <a href="https://github.com/smpallen99/ex_admin"target="_blank" rel="noopener">Ex Admin</a>, that&rsquo;s been under heavy development since at least May of 2015. It&rsquo;s so damn close to ActiveAdmin that its old theme will make you forget you&rsquo;re not in a Rails application.</p>
<p>Again, it&rsquo;s pretty straightforward to set it up by just following their <a href="https://github.com/smpallen99/ex_admin"target="_blank" rel="noopener">README</a> instructions.</p>
<p>Once you have it installed and configured, you can very quickly expose the User model into the Admin interface like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix admin.gen.resource User</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And we can edit the <code>web/admin/user.ex</code> with the following:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">YourApp</span><span class="o">.</span><span class="n">ExAdmin</span><span class="o">.</span><span class="n">User</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExAdmin</span><span class="o">.</span><span class="n">Register</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">register_resource</span> <span class="no">YourApp</span><span class="o">.</span><span class="n">User</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">index</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">selectable_column</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">column</span> <span class="ss">:id</span>
</span></span><span class="line"><span class="cl">      <span class="n">column</span> <span class="ss">:name</span>
</span></span><span class="line"><span class="cl">      <span class="n">column</span> <span class="ss">:email</span>
</span></span><span class="line"><span class="cl">      <span class="n">column</span> <span class="ss">:last_sign_in_at</span>
</span></span><span class="line"><span class="cl">      <span class="n">column</span> <span class="ss">:last_sign_in_ip</span>
</span></span><span class="line"><span class="cl">      <span class="n">column</span> <span class="ss">:sign_in_count</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">show</span> <span class="n">_user</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">attributes_table</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="n">row</span> <span class="ss">:id</span>
</span></span><span class="line"><span class="cl">        <span class="n">row</span> <span class="ss">:name</span>
</span></span><span class="line"><span class="cl">        <span class="n">row</span> <span class="ss">:email</span>
</span></span><span class="line"><span class="cl">        <span class="n">row</span> <span class="ss">:reset_password_token</span>
</span></span><span class="line"><span class="cl">        <span class="n">row</span> <span class="ss">:reset_password_sent_at</span>
</span></span><span class="line"><span class="cl">        <span class="n">row</span> <span class="ss">:locked_at</span>
</span></span><span class="line"><span class="cl">        <span class="n">row</span> <span class="ss">:unlock_token</span>
</span></span><span class="line"><span class="cl">        <span class="n">row</span> <span class="ss">:sign_in_count</span>
</span></span><span class="line"><span class="cl">        <span class="n">row</span> <span class="ss">:current_sign_in_at</span>
</span></span><span class="line"><span class="cl">        <span class="n">row</span> <span class="ss">:last_sign_in_at</span>
</span></span><span class="line"><span class="cl">        <span class="n">row</span> <span class="ss">:current_sign_in_ip</span>
</span></span><span class="line"><span class="cl">        <span class="n">row</span> <span class="ss">:last_sign_in_ip</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">form</span> <span class="n">user</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">inputs</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="n">input</span> <span class="n">user</span><span class="p">,</span> <span class="ss">:name</span>
</span></span><span class="line"><span class="cl">        <span class="n">input</span> <span class="n">user</span><span class="p">,</span> <span class="ss">:email</span>
</span></span><span class="line"><span class="cl">        <span class="n">input</span> <span class="n">user</span><span class="p">,</span> <span class="ss">:password</span><span class="p">,</span> <span class="ss">type</span><span class="p">:</span> <span class="ss">:password</span>
</span></span><span class="line"><span class="cl">        <span class="n">input</span> <span class="n">user</span><span class="p">,</span> <span class="ss">:password_confirmation</span><span class="p">,</span> <span class="ss">type</span><span class="p">:</span> <span class="ss">:password</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Yes, this is eerily similar to the ActiveAdmin DSL. Thumbs up for the team responsible, and it really shows how Elixir is well suited for Domain Specific Languages, if you&rsquo;re into that.</p>
<p>If you followed the Coherence instructions, it asks you to add a <code>:protected</code> pipeline (a set of plugs) for your protected routes. For now you can add the <code>/admin</code> route to go through that pipeline. And for the uninitiated, a &ldquo;plug&rdquo; is similar in concept to a Rack app, or more specifically, a Rails middleware. But in Rails we only have one pipeline of middlewares. In Phoenix we can configure multiple pipelines for different sets of routes (browser and api, for example).</p>
<p>So we can add the following to <code>web/router.ex</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">scope</span> <span class="s2">&#34;/admin&#34;</span><span class="p">,</span> <span class="no">ExAdmin</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">pipe_through</span> <span class="ss">:protected</span>
</span></span><span class="line"><span class="cl">  <span class="n">admin_routes</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>With those simple contraptions in place, you will end up with something like this:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/574/Screen_Shot_2016-12-06_at_17.56.17.png" alt="Ex Admin"  loading="lazy" /></p>
<p>And if you&rsquo;re still not convinced, how about changing to their old theme?</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/575/Screen_Shot_2016-12-06_at_17.56.25.png" alt="ActiveAdmin knockoff"  loading="lazy" /></p>
<p>Hell yeah! Makes me feel right at home, although I really prefer the new theme. But you could replace your ActiveAdmin-based app for this one and your users would hardly notice the small differences in the interface. The behavior is basically the same.</p>
<p>If you still have questions on how to properly configure ExAdmin, check out their <a href="https://github.com/smpallen99/contact_demo"target="_blank" rel="noopener">Contact Demo</a> project, where you can find a real example.</p>
<h3>Stitching a simple Admin role<span class="hx:absolute hx:-mt-20" id="stitching-a-simple-admin-role"></span>
    <a href="#stitching-a-simple-admin-role" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Obviously, we don&rsquo;t want to let all authenticated users access the Admin section.</p>
<p>So we can add a simple boolean field in the users table to indicate whether a user is an admin or not. You can change your migration to resemble this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">change</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">create</span> <span class="n">table</span><span class="p">(</span><span class="ss">:users</span><span class="p">,</span> <span class="ss">primary_key</span><span class="p">:</span> <span class="kp">false</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">add</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">    <span class="n">add</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="n">add</span> <span class="ss">:admin</span><span class="p">,</span> <span class="ss">:boolean</span><span class="p">,</span> <span class="ss">default</span><span class="p">:</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And you can configure the <code>priv/repos/seeds.exs</code> file to create 2 users, one admin and one guest:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">YourApp</span><span class="o">.</span><span class="n">Repo</span><span class="o">.</span><span class="n">delete_all</span> <span class="no">YourApp</span><span class="o">.</span><span class="n">User</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">YourApp</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">changeset</span><span class="p">(</span><span class="o">%</span><span class="no">YourApp</span><span class="o">.</span><span class="n">User</span><span class="p">{},</span> <span class="sx">%{name: &#34;Administrator&#34;, email: &#34;admin@example.org&#34;, password: &#34;password&#34;, password_confirmation: &#34;password&#34;, admin: true}</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">|&gt;</span> <span class="no">YourApp</span><span class="o">.</span><span class="n">Repo</span><span class="o">.</span><span class="n">insert!</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">YourApp</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">changeset</span><span class="p">(</span><span class="o">%</span><span class="no">YourApp</span><span class="o">.</span><span class="n">User</span><span class="p">{},</span> <span class="sx">%{name: &#34;Guest&#34;, email: &#34;guest@example.org&#34;, password: &#34;password&#34;, password_confirmation: &#34;password&#34;, admin: false}</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">|&gt;</span> <span class="no">YourApp</span><span class="o">.</span><span class="n">Repo</span><span class="o">.</span><span class="n">insert!</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As this is just an exercise, you can drop the database and recreate it, like this: <code>mix do ecto.drop, ecto.setup</code>.</p>
<p>Coherence takes care of <strong>authentication</strong>, but we need to take care of <strong>authorization</strong>. You will find many examples online to something that resembles Rails&rsquo; Pundit, such as <a href="https://github.com/schrockwell/bodyguard"target="_blank" rel="noopener">Bodyguard</a>. But for this post I will stick to a simple Plug and create a new Router pipeline.</p>
<p>We need to create <code>lib/your_app/plugs/authorized.ex</code> and add the following:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">YourApp</span><span class="o">.</span><span class="n">Plugs</span><span class="o">.</span><span class="n">Authorized</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@behaviour</span> <span class="no">Plug</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">import</span> <span class="no">Plug</span><span class="o">.</span><span class="n">Conn</span>
</span></span><span class="line"><span class="cl">  <span class="n">import</span> <span class="no">Phoenix</span><span class="o">.</span><span class="n">Controller</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">default</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="n">default</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="sx">%{assigns: %{current_user: current_user}}</span> <span class="o">=</span> <span class="n">conn</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">current_user</span><span class="o">.</span><span class="n">admin</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span>
</span></span><span class="line"><span class="cl">        <span class="o">|&gt;</span> <span class="n">flash_and_redirect</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">flash_and_redirect</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">flash_and_redirect</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">put_flash</span><span class="p">(</span><span class="ss">:error</span><span class="p">,</span> <span class="s2">&#34;You do not have the proper authorization to do that&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">redirect</span><span class="p">(</span><span class="ss">to</span><span class="p">:</span> <span class="s2">&#34;/&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">halt</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Once a user signs in, Coherence puts the structure of the authenticated user into the <code>conn</code> (a Plug.Conn structure), so we can pattern match from it.</p>
<p>Now we need to create the router pipeline in the <code>web/router.ex</code> like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">pipeline</span> <span class="ss">:protected_admin</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="ss">:accepts</span><span class="p">,</span> <span class="o">[</span><span class="s2">&#34;html&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="ss">:fetch_session</span>
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="ss">:fetch_flash</span>
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="ss">:protect_from_forgery</span>
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="ss">:put_secure_browser_headers</span>
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="no">Coherence</span><span class="o">.</span><span class="n">Authentication</span><span class="o">.</span><span class="n">Session</span><span class="p">,</span> <span class="kp">protected</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="no">YourApp</span><span class="o">.</span><span class="n">Plugs</span><span class="o">.</span><span class="n">Authorized</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">scope</span> <span class="s2">&#34;/&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">pipe_through</span> <span class="ss">:protected_admin</span>
</span></span><span class="line"><span class="cl">  <span class="n">coherence_routes</span> <span class="ss">:protected_admin</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">scope</span> <span class="s2">&#34;/admin&#34;</span><span class="p">,</span> <span class="no">ExAdmin</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">pipe_through</span> <span class="ss">:protected_admin</span>
</span></span><span class="line"><span class="cl">  <span class="n">admin_routes</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <code>:protected_admin</code> pipeline is exactly the same as <code>:protected</code> but we add the newly created <code>YourApp.Plugs.Authorized</code> plug at the end. And then we change the <code>/admin</code> scope to go through this new pipeline.</p>
<p>And that&rsquo;s it. If you log in with the <code>guest@example.org</code> user, it will be kicked out to the homepage with a message saying that it&rsquo;s not authorized. If you log in with the <code>admin@example.org</code> it will be able to access the ExAdmin interface in <code>/admin</code>.</p>
<h3>Wrapping Up<span class="hx:absolute hx:-mt-20" id="wrapping-up"></span>
    <a href="#wrapping-up" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Even though it&rsquo;s now super simple to add Authentication, Administration and basic Authorization, don&rsquo;t be fooled, the learning curve is still steep, even if you&rsquo;ve been a Rails developer for a while.</p>
<p>Because of what&rsquo;s underneath, the OTP architecture, the concepts of Applications, Supervisors, Workers, etc, it&rsquo;s not immediately simple to wrap your head around what&rsquo;s <strong>really</strong> going on. If you&rsquo;re not careful, libraries such as Coherence or ExAdmin will make you feel like it&rsquo;s just as simple as Rails.</p>
<p>And it&rsquo;s not like that. Elixir is a completely different beast. And I mean it in a bad way, on the contrary. It&rsquo;s meant for highly reliable and distributed systems and it demands way more knowledge, patience and training from the programmer.</p>
<p>On the other hand, exactly because libraries such as Coherence make it a lot easier to get started, you may become more motivated to put something up and running and then investing more time really <strong>understanding</strong> what&rsquo;s going on underneath. So the recommendation is: get your hands dirty, get some quick instant gratification of seeing something running, and then go on and refine your knowledge. It will be way more rewarding if you do so.</p>
<p>I don&rsquo;t see Phoenix as meant to just be a Rails replacement. This would be too easy. I see it more as another piece to make Elixir the best suited set of technologies to build <strong>highly scalable, highly reliable, highly distributed systems</strong>. Stopping at simple web applications would not fulfill Elixir&rsquo;s potential.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>backend</category><category>security</category></item><item><title>Elixir Pipe Operator for Ruby: Chainable Methods</title><link>https://akitaonrails.github.io/en/2016/02/18/elixir-pipe-operator-for-ruby-chainable-methods/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/02/18/elixir-pipe-operator-for-ruby-chainable-methods/</guid><pubDate>Thu, 18 Feb 2016 16:28:00 -0200</pubDate><description>&lt;p&gt;There have been &lt;a href="http://blog.molawson.com/elixir-pipes-in-ruby/"target="_blank" rel="noopener"&gt;recent&lt;/a&gt; &lt;a href="https://gist.github.com/pcreux/2f87847e5e4aad37db02"target="_blank" rel="noopener"&gt;discussions&lt;/a&gt; about how nice it would be to have something like the awesome Elixir Pipe Operator for Ruby.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t know what the &amp;ldquo;Pipe Operator&amp;rdquo; is in Elixir, take the following code:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&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;filter&lt;/span&gt;&lt;span class="p"&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="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&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;odd?&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;It&amp;rsquo;s ugly, we all know that. In an Object Oriented language like Ruby we would do something like this:&lt;/p&gt;</description><content:encoded><![CDATA[<p>There have been <a href="http://blog.molawson.com/elixir-pipes-in-ruby/"target="_blank" rel="noopener">recent</a> <a href="https://gist.github.com/pcreux/2f87847e5e4aad37db02"target="_blank" rel="noopener">discussions</a> about how nice it would be to have something like the awesome Elixir Pipe Operator for Ruby.</p>
<p>If you don&rsquo;t know what the &ldquo;Pipe Operator&rdquo; is in Elixir, take the following code:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Enum</span><span class="o">.</span><span class="n">sum</span><span class="p">(</span><span class="no">Enum</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">100_000</span><span class="p">,</span> <span class="o">&amp;</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span> <span class="o">*</span> <span class="mi">3</span><span class="p">)),</span> <span class="n">odd?</span><span class="p">))</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s ugly, we all know that. In an Object Oriented language like Ruby we would do something like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">100_000</span><span class="p">)</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">i</span> <span class="o">*</span> <span class="mi">3</span> <span class="p">}</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">select</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:odd?</span><span class="p">)</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">reduce</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:+</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>But Elixir does not have objects, only functions, so how can we code more elegantly? The solution came up in the form of the so called &ldquo;Pipe Operator&rdquo; which takes the last returning value and passes it through as the first argument of the next function call, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="mi">1</span><span class="o">..</span><span class="mi">100_000</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Stream</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span> <span class="o">*</span> <span class="mi">3</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Stream</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">odd?</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">sum</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So Ruby and Elixir &ldquo;feel&rdquo; the same when we are able to &ldquo;chain&rdquo; methods. In the Ruby world we don&rsquo;t have the &ldquo;need&rdquo; for an operator like that. But it would be nice to have a mechanism that we could use to make our code more expressive, or more testable, or more readable. For example, what if we could write something like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">100_000</span><span class="p">)</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">multiple_each_element_by_three</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">filter_out_odd_elements</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">give_the_sum_of_all_elements</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Of course, this is a very constrained example with really bad method naming. But if we look at Mo Lawson&rsquo;s article I linked above, it becomes more interesting:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">keywords</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">kw</span><span class="o">|</span> <span class="n">kw</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/(^[^[:alpha:]]+|[^[:alpha:]]+$)/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">kw</span><span class="o">|</span> <span class="no">LegacySpanishCorrector</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">correct</span><span class="p">(</span><span class="n">kw</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">kw</span><span class="o">|</span> <span class="n">kw</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/[^[:alpha:]\d&#39;_\-\/]/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">reject</span> <span class="p">{</span> <span class="o">|</span><span class="n">kw</span><span class="o">|</span> <span class="no">STOP_WORDS</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">kw</span><span class="p">)</span> <span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ruby allows us to chain Enumerable methods one after the other, transforming the initial keywords list into &ldquo;something&rdquo; that is very difficult to infer just by looking at this code.</p>
<p>What about this other version?</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">KeywordNormalizer</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="n">keywords</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Collection</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">keywords</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">strip_outer_punctuation</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">fix_spanish</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">clean_inner_punctuation</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">remove_stop_words</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">to_a</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is where he ends up by the end of his article: much more readable and each new isolated method is unit testable, resulting in more robust code.</p>
<p>The whole idea of this post is to introduce you to my new little gem <a href="https://rubygems.org/gems/chainable_methods"target="_blank" rel="noopener">&ldquo;Chainable Methods&rdquo;</a>. The <a href="https://github.com/akitaonrails/chainable_methods"target="_blank" rel="noopener">source code</a> is on Github, as usual, so please contribute.</p>
<p>My gem will allow you to write Lawson&rsquo;s last code like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">KeywordNormalizer</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">chain_from</span><span class="p">(</span><span class="n">keywords</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">strip_outer_punctuation</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">fix_spanish</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">clean_inner_punctuation</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">remove_stop_words</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">to_a</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">unwrap</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You add the <tt>chainable_methods</tt> to your Gemfile as usual (you know the drill), then you can write Lawson&rsquo;s module like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">KeywordNormalizer</span>
</span></span><span class="line"><span class="cl">  <span class="kp">extend</span> <span class="no">ChainableMethods</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">strip_outer_punctuation</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">array</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">el</span><span class="o">|</span> <span class="n">el</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/(^[^[:alpha:]]+|[^[:alpha:]]+$)/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">fix_spanish</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">array</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">el</span><span class="o">|</span> <span class="no">LegacySpanishCorrector</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">correct</span><span class="p">(</span><span class="n">el</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">clean_inner_punctuation</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">array</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">el</span><span class="o">|</span> <span class="n">el</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/[^[:alpha:]\d&#39;_\-\/]/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">remove_stop_words</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">array</span><span class="o">.</span><span class="n">reject</span> <span class="p">{</span> <span class="o">|</span><span class="n">el</span><span class="o">|</span> <span class="no">STOP_WORDS</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">el</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And that&rsquo;s it, now you can chain everything like I showed previously. The pattern is:</p>
<ol>
<li>Write a Module with class-level methods that receive at least one argument and extend the &lsquo;ChainableMethods&rsquo; module:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">MyModule</span>
</span></span><span class="line"><span class="cl">  <span class="kp">extend</span> <span class="no">ChainableMethods</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">method_a</span><span class="p">(</span><span class="n">argument1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># ...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">method_b</span><span class="p">(</span><span class="n">argument1</span><span class="p">,</span> <span class="n">argument2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># ...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">method_c</span><span class="p">(</span><span class="n">argument1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># yield a block passing the received argument</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<ol start="2">
<li>Wrap an initial state that will be passed to the first method as its first argument:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">some_initial_state</span> <span class="o">=</span> <span class="s2">&#34;Hello World&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">MyModule</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">chain_from</span><span class="p">(</span><span class="n">some_initial_state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># ...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<ol start="3">
<li>Chain as many methods from the module or methods that the returning state recognizes:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">MyModule</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">chain_from</span><span class="p">(</span><span class="n">some_initial_state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">upcase</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">method_a</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">method_b</span><span class="p">(</span><span class="n">argument2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">method_c</span> <span class="p">{</span> <span class="o">|</span><span class="n">foo</span><span class="o">|</span> <span class="n">foo</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;, &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">unwrap</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice that we do not need to pass the first argument to the methods within the &lsquo;MyModule&rsquo; module, it will get the result from the last call automatically.</p>
<ol start="4">
<li>Do not forget to call <tt>#unwrap</tt> as the last call to get the last result from the chain.</li>
</ol>
<h3>An Experiment<span class="hx:absolute hx:-mt-20" id="an-experiment"></span>
    <a href="#an-experiment" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>And that&rsquo;s it! I isolated this behavior only into modules that explicitly extend the &lsquo;ChainableMethods&rsquo; module instead of automagically enabling it at the BasicObject level as many would initially think, because we don&rsquo;t want a global &lsquo;method_missing&rsquo; dangling around unchecked.</p>
<p>This behavior makes use of &lsquo;method_missing&rsquo; so it&rsquo;s not going to be fast in a synthetic benchmark against a direct method call, for obvious reasons. The purpose is not to be fast, just expressive. So keep that in mind.</p>
<p>The use case is: whenever you have some kind of transformation, you will want a chain of unit testable, isolated functions, and this is how you can get it without too much hassle.</p>
<p>This is an experiment. Because I&rsquo;m using &lsquo;method_missing&rsquo; there may be side-effects I am not seeing right now, so please let me know in the <a href="https://github.com/akitaonrails/chainable_methods/issues"target="_blank" rel="noopener">Github Issues</a> and send feedback if it helped you out in some project.</p>
<p>Pull Requests are most welcome!</p>
]]></content:encoded><category>ruby</category><category>elixir-and-erlang</category><category>programming-languages</category></item><item><title>Ex Pusher Lite - Part 2 - First Working Core!</title><link>https://akitaonrails.github.io/en/2015/12/14/ex-pusher-lite-part-2-first-working-core/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/14/ex-pusher-lite-part-2-first-working-core/</guid><pubDate>Mon, 14 Dec 2015 15:04:00 -0200</pubDate><description>&lt;p&gt;In &lt;a href="http://www.akitaonrails.com/en/2015/12/09/ex-pusher-lite-parte-1-phoenix-channels-e-aplicacoes-rails"target="_blank" rel="noopener"&gt;Part 1&lt;/a&gt; I basically started with &lt;a href="https://labs.opendoor.com/phoenix-on-rails-for-client-push-notifications"target="_blank" rel="noopener"&gt;Daniel Neighman&amp;rsquo;s&lt;/a&gt; tutorial.&lt;/p&gt;
&lt;p&gt;In Part 2 I will add the proper mechanisms to make a minimal core that is actually useful and deploy it to Heroku. In order to do that I need to implement the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a simple Administration authentication (a hardcoded admin_username and admin_password will do for now)&lt;/li&gt;
&lt;li&gt;an admin restricted &lt;strong&gt;&amp;quot;/api/admin/apps&amp;quot;&lt;/strong&gt; endpoint to manage new Applications. Each Application should have a randomly generated key and secret.&lt;/li&gt;
&lt;li&gt;the existing &amp;ldquo;/events&amp;rdquo; endpoint from Part 1 should be moved to &lt;strong&gt;&amp;quot;/api/apps/:app_id/events&amp;quot;&lt;/strong&gt; and have access restricted to the authentication of the key and secret of the Application identified as &amp;ldquo;app_id&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;for now, the Event will just broadcast to the appropriate topic. We want to be able to broadcast to everyone from an Application as well to specific topics within the Application scope.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As usual, the code for this section will be tagged as &lt;strong&gt;v0.2&lt;/strong&gt; in both the &lt;a href="https://github.com/akitaonrails/pusher_lite_demo/tree/v0.2"target="_blank" rel="noopener"&gt;client demo&lt;/a&gt; and &lt;a href="https://github.com/akitaonrails/ex_pusher_lite/tree/v0.2"target="_blank" rel="noopener"&gt;server-side&lt;/a&gt; Github repositories.&lt;/p&gt;</description><content:encoded><![CDATA[<p>In <a href="http://www.akitaonrails.com/en/2015/12/09/ex-pusher-lite-parte-1-phoenix-channels-e-aplicacoes-rails"target="_blank" rel="noopener">Part 1</a> I basically started with <a href="https://labs.opendoor.com/phoenix-on-rails-for-client-push-notifications"target="_blank" rel="noopener">Daniel Neighman&rsquo;s</a> tutorial.</p>
<p>In Part 2 I will add the proper mechanisms to make a minimal core that is actually useful and deploy it to Heroku. In order to do that I need to implement the following:</p>
<ul>
<li>a simple Administration authentication (a hardcoded admin_username and admin_password will do for now)</li>
<li>an admin restricted <strong>&quot;/api/admin/apps&quot;</strong> endpoint to manage new Applications. Each Application should have a randomly generated key and secret.</li>
<li>the existing &ldquo;/events&rdquo; endpoint from Part 1 should be moved to <strong>&quot;/api/apps/:app_id/events&quot;</strong> and have access restricted to the authentication of the key and secret of the Application identified as &ldquo;app_id&rdquo;.</li>
<li>for now, the Event will just broadcast to the appropriate topic. We want to be able to broadcast to everyone from an Application as well to specific topics within the Application scope.</li>
</ul>
<p>As usual, the code for this section will be tagged as <strong>v0.2</strong> in both the <a href="https://github.com/akitaonrails/pusher_lite_demo/tree/v0.2"target="_blank" rel="noopener">client demo</a> and <a href="https://github.com/akitaonrails/ex_pusher_lite/tree/v0.2"target="_blank" rel="noopener">server-side</a> Github repositories.</p>
<h3>The App Resource<span class="hx:absolute hx:-mt-20" id="the-app-resource"></span>
    <a href="#the-app-resource" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>If this project of ours is to behave like Pusher.com, we need a way to create new &ldquo;Applications&rdquo;. Each client connecting to this service will be bound to this Application. Events should be restricted to the Application boundary. This is how we will isolate different clients connecting to the same server. So you can have one core serving several different web applications.</p>
<p>Once a new application is created, the client/consumer web app will have the pair of key and secret tokens that it will use to connect both the server-side triggers as well as the client-side Websocket listeners.</p>
<p>As a disclaimer, at this stage of development I will not implement any sophisticated authentication system such as OAuth2 or JWT. I will save this for posts to follow. For now I will use the Application&rsquo;s key and secret just as simple username and password in an HTTP Basic Auth. This should be good enough for our purposes for the time being.</p>
<p>So, the very first step is to create such an &ldquo;Application&rdquo; resource and we can resort to Phoenix&rsquo;s built-in JSON scaffold generator:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix phoenix.gen.json App apps name:string slug:string key:string secret:string active:boolean</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Most tutorials will show you the &ldquo;<tt>phoenix.gen.html</tt>&rdquo; generator, which behaves like Rails&rsquo;s &ldquo;scaffold&rdquo;, generating HTML templates for each of the CRUD verbs. This is similar but it skips HTML and assumes this is going to be a JSON CRUD API.</p>
<p>We need to manually update the &ldquo;<tt>web/router.ex</tt>&rdquo; file like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/router.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">scope</span> <span class="s2">&#34;/api&#34;</span><span class="p">,</span> <span class="no">ExPusherLite</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">pipe_through</span> <span class="ss">:api</span>
</span></span><span class="line"><span class="cl">  <span class="n">post</span> <span class="s2">&#34;/apps/:app_slug/events&#34;</span><span class="p">,</span> <span class="no">EventsController</span><span class="p">,</span> <span class="ss">:create</span>
</span></span><span class="line"><span class="cl">  <span class="n">scope</span> <span class="s2">&#34;/admin&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">resources</span> <span class="s2">&#34;/apps&#34;</span><span class="p">,</span> <span class="no">AppController</span><span class="p">,</span> <span class="ss">except</span><span class="p">:</span> <span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:edit</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The &ldquo;<tt>EventsController</tt>&rdquo; is the one we implemented in Part 1 and that we will overhaul during this Part 2.</p>
<p>The generator gave us this new &ldquo;<tt>AppController</tt>&rdquo;, and similarly to Rails&rsquo; routes, the DSL is remarkably similar here. If you&rsquo;re a Railer I bet you can instantly recognize the routes this DSL is generating.</p>
<p>The generator also created a proper migration for us:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># priv/repo/migrations/20151210131528_create_app.exs</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Repo</span><span class="o">.</span><span class="n">Migrations</span><span class="o">.</span><span class="n">CreateApp</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Ecto</span><span class="o">.</span><span class="n">Migration</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">change</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">create</span> <span class="n">table</span><span class="p">(</span><span class="ss">:apps</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">add</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">      <span class="n">add</span> <span class="ss">:slug</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">      <span class="n">add</span> <span class="ss">:key</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">      <span class="n">add</span> <span class="ss">:secret</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">      <span class="n">add</span> <span class="ss">:active</span><span class="p">,</span> <span class="ss">:boolean</span><span class="p">,</span> <span class="ss">default</span><span class="p">:</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">timestamps</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">create</span> <span class="n">index</span><span class="p">(</span><span class="ss">:apps</span><span class="p">,</span> <span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span> <span class="ss">unique</span><span class="p">:</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">create</span> <span class="n">index</span><span class="p">(</span><span class="ss">:apps</span><span class="p">,</span> <span class="o">[</span><span class="ss">:slug</span><span class="o">]</span><span class="p">,</span> <span class="ss">unique</span><span class="p">:</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Again, remarkably similar to ActiveRecord&rsquo;s Migration DSL. Migrations behave as you expect. You must run:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix ecto.create # if you haven&#39;t already
mix ecto.migrate</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This App resource will need the ability to create slugs out of the names (which we will use as &ldquo;app_id&rdquo;) and also generate random key and secret values. So we must add these dependencies to the &ldquo;<tt>mix.exs</tt>&rdquo; file:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># mix.exs</span>
</span></span><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">deps</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">[...</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="ss">:secure_random</span><span class="p">,</span> <span class="s2">&#34;~&gt; 0.2.0&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="ss">:slugger</span><span class="p">,</span> <span class="s2">&#34;~&gt; 0.0.1&#34;</span><span class="p">}</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <a href="https://github.com/akitaonrails/ex_pusher_lite/blob/v0.2/web/models/app.ex"target="_blank" rel="noopener">final App model</a> is quite long, so I will break it down for you:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/models/app.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">App</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:model</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Repo</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">schema</span> <span class="s2">&#34;apps&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:slug</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:key</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:secret</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:active</span><span class="p">,</span> <span class="ss">:boolean</span><span class="p">,</span> <span class="ss">default</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">timestamps</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="vi">@required_fields</span> <span class="o">~</span><span class="n">w</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@optional_fields</span> <span class="o">~</span><span class="n">w</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This block declares the model Schema. Be careful if you generate a migration and then change its fields settings: you must remember to update the schema in the model. In my first attempt I didn&rsquo;t include the &ldquo;slug&rdquo; field, so I rolled back the database migration (with &ldquo;<tt>mix ecto.rollback</tt>&rdquo;), changed the migration to add the &ldquo;slug&rdquo; field and re-ran the &ldquo;<tt>ecto.migrate</tt>&rdquo; task.</p>
<p>I was puzzled with the model not picking up the new field; after some time I remembered that Ecto models don&rsquo;t attempt to fetch the real database schema and generate accessors dynamically, instead it relies on the explicitly declared schema block as shown above. After I added the new &ldquo;slug&rdquo; field in the schema block, then the model would properly use the new field.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/models/app.ex</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_by_slug</span><span class="p">(</span><span class="n">slug</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Repo</span><span class="o">.</span><span class="n">get_by!</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="ss">slug</span><span class="p">:</span> <span class="n">slug</span><span class="p">,</span> <span class="ss">active</span><span class="p">:</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">hashed_secret</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Base</span><span class="o">.</span><span class="n">encode64</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="n">model</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">model</span><span class="o">.</span><span class="n">secret</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>These are just helper functions to use in the AppController. The odd bit might be &ldquo;<strong>MODULE</strong>&rdquo; but this is just a shortcut for the atom representation of the current module, which is &ldquo;<tt>ExPusherLite.App</tt>&rdquo;. This is how you make a simple query to the model, it resembles Rails&rsquo; &ldquo;<tt>App.get_by(slug: slug, active: true)</tt>&rdquo;.</p>
<p>In Elixir convention, Ecto has functions with and without bangs (&quot;<tt>get_by!</tt>&quot; and &ldquo;<tt>get_by</tt>&rdquo;). If you want to catch an error you use the version without bangs and it will return either a &ldquo;<tt>{:ok, result}</tt>&rdquo; tuple or a &ldquo;<tt>{:error, result}</tt>&rdquo; and you can pattern match them. Or you can use the bang version and it will raise an exception. Depends on what you want to do.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/models/app.ex</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">changeset</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">params</span> <span class="p">\\</span> <span class="ss">:empty</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">model</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">(</span><span class="n">params</span><span class="p">,</span> <span class="vi">@required_fields</span><span class="p">,</span> <span class="vi">@optional_fields</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">validate_length</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">min</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="ss">max</span><span class="p">:</span> <span class="mi">255</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">unique_constraint</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">generate_key</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">generate_secret</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">slugify</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">unique_constraint</span><span class="p">(</span><span class="ss">:slug</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Second only to the Schema block I mentioned above, this &ldquo;<tt>changeset/2</tt>&rdquo; function is the most important part of a Model.</p>
<p>In Rails you just have the concept of a &ldquo;Model&rdquo; which is considered &ldquo;Fat&rdquo; because it deals with database operations, business logic and framework hooks all in the same place. In Phoenix you have to deal with at least 3 different concepts:</p>
<ul>
<li>You have a <strong>Repo</strong>, which receives a Changeset and uses it to insert or update the designated rows in the database table. You will see &ldquo;<tt>Repo.get</tt>&rdquo; or &ldquo;<tt>Repo.insert</tt>&rdquo;, not &ldquo;<tt>App.find</tt>&rdquo; or &ldquo;<tt>App.save</tt>&rdquo;.</li>
<li>Then you have the <strong>Changeset</strong> which are just Elixir Maps (the ones with the syntax &ldquo;%<tt>{key =&gt; value}</tt>&rdquo;). It&rsquo;s just a Hash, a Dictionary, a collection of key-value pairs. A Repo accepts Maps for its operations.</li>
<li>Finally, you have the <strong>Model</strong> which actually give meaning and context to the raw Changesets. And the function &ldquo;<tt>changeset/2</tt>&rdquo; above is the one responsible for receiving a raw Map, piping it through a validation and transformation chain and return a valid Changeset for the Repo to use.</li>
</ul>
<p>So, in a controller you will usually find code like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/controllers/app_controller.ex</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="sx">%{&#34;app&#34; =&gt; app_params}</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">changeset</span> <span class="o">=</span> <span class="no">App</span><span class="o">.</span><span class="n">changeset</span><span class="p">(</span><span class="o">%</span><span class="no">App</span><span class="p">{},</span> <span class="n">app_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="no">Repo</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="n">changeset</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">app</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:error</span><span class="p">,</span> <span class="n">changeset</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is how you create a new, validated, changeset and then pass it to the Repo, treating the results in a pattern match block. Just compare the above changeset line with the beginning of the &ldquo;changeset/2&rdquo; function:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">changeset</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">params</span> <span class="p">\\</span> <span class="ss">:empty</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">model</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">(</span><span class="n">params</span><span class="p">,</span> <span class="vi">@required_fields</span><span class="p">,</span> <span class="vi">@optional_fields</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">validate_length</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">min</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="ss">max</span><span class="p">:</span> <span class="mi">255</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It maps the &ldquo;<tt>%App{}</tt>&rdquo; empty record to the &ldquo;model&rdquo; argument and the &ldquo;app_params&rdquo; that comes from the request (a map of the format &ldquo;<tt>%{name =&gt; &lsquo;foo&rsquo;, active: =&gt; &rsquo;true&rsquo;}</tt>&rdquo;) to the argument &ldquo;params&rdquo;. Then it pipes the model and params to the &ldquo;<tt>cast/4</tt>&rdquo; function which will copy the values from the params map to the model map/changeset. And it keep passing the resulting changeset to the following functions, such as &ldquo;<tt>validated_length/3</tt>&rdquo; below, and so on. If the chain ends with no exceptions, you end up with a clean, validated changeset that you can just pass to the Repo to blindly insert to the database.</p>
<p>In the above implementation we are chaining filters to generate the key, secret and slug, and this is the implementation as private functions:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/models/app.ex</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">generate_key</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">get_field</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="ss">:key</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">model</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">model</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">put_change</span><span class="p">(</span><span class="ss">:key</span><span class="p">,</span> <span class="no">SecureRandom</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">generate_secret</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">get_field</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="ss">:secret</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">model</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">model</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">put_change</span><span class="p">(</span><span class="ss">:secret</span><span class="p">,</span> <span class="no">SecureRandom</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">slugify</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">name</span> <span class="o">=</span> <span class="n">get_change</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="ss">:name</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">model</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">put_change</span><span class="p">(</span><span class="ss">:slug</span><span class="p">,</span> <span class="no">Slugger</span><span class="o">.</span><span class="n">slugify_downcase</span><span class="p">(</span><span class="nb">name</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">model</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The logic is set so new key/secret are generated only if the fields are empty and a new slug is generated only if the name has changes. And this is it, I told you the model code would be a bit large. You can see how to use the Slugger and SecureRandom libraries we added in the &ldquo;mix.exs&rdquo; before.</p>
<p>I also want to add the equivalent of a Rails seed file to create a test application so it&rsquo;s easier for newcomers to know what to do. Phoenix has seeds and you can implement it like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># priv/repo/seeds.exs</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">alias</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">App</span>
</span></span><span class="line"><span class="cl"><span class="k">alias</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Repo</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># not using the App.changeset should just avoid all validations and generations</span>
</span></span><span class="line"><span class="cl"><span class="no">Repo</span><span class="o">.</span><span class="n">insert!</span> <span class="o">%</span><span class="no">App</span><span class="p">{</span> <span class="ss">slug</span><span class="p">:</span> <span class="s2">&#34;test-app&#34;</span><span class="p">,</span> <span class="nb">name</span><span class="p">:</span> <span class="s2">&#34;Test App&#34;</span><span class="p">,</span> <span class="ss">key</span><span class="p">:</span> <span class="s2">&#34;test-app-fake-key&#34;</span><span class="p">,</span> <span class="ss">secret</span><span class="p">:</span> <span class="s2">&#34;test-app-fake-secret&#34;</span><span class="p">,</span> <span class="ss">active</span><span class="p">:</span> <span class="kp">true</span> <span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Remember how I detailed the role of the &ldquo;changeset/2&rdquo; function in creating a clean and validated changeset, which is just a Map? You can skip that function altogether and hand craft your own final Map and pass it to the Repo. The Repo doesn&rsquo;t care if this is a valid Map or not it will just try to insert it into the database regardless. And in this case the App Model prevents us from hardcoding keys and secrets, so this is how we do it in a seed file.</p>
<p>We can just run it directly like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix run priv/repo/seeds.exs</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The AppController just needs 2 changes. The first is to search the App through the slug field instead of the default &lsquo;id&rsquo; field. This is simple enough, we just replace all calls to &ldquo;<tt>app = Repo.get!(App, id)</tt>&rdquo; to &ldquo;<tt>app = App.get_by_slug(id)</tt>&rdquo;, which is why we implemented this function in the model above.</p>
<p>The second thing is Authentication.</p>
<h3>Adding Authentication<span class="hx:absolute hx:-mt-20" id="adding-authentication"></span>
    <a href="#adding-authentication" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Now that we have an App model that can generate secure random UUIDs for key and secret, I will add a second level of authentication for administrators to be able to create such Apps.</p>
<p>For that I will just hard-code a secret in the config file of the application itself to serve as a development default. Like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/config.exs</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="ss">:admin_authentication</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">username</span><span class="p">:</span> <span class="s2">&#34;pusher_admin_username&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">password</span><span class="p">:</span> <span class="s2">&#34;pusher_admin_password&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">import_config</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">Mix</span><span class="o">.</span><span class="n">env</span><span class="si">}</span><span class="s2">.exs&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You must add this block before the &ldquo;<tt>import_config</tt>&rdquo; function. Then you can override those values in the &ldquo;config/prod.secret.exs&rdquo; file, for example, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/prod.secret.exs</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="ss">:admin_authentication</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">username</span><span class="p">:</span> <span class="s2">&#34;14e86e5fee3335fa88b0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">password</span><span class="p">:</span> <span class="s2">&#34;2b94ff0f07ce9769567f&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Of course, generate your own pair of secure username and password and replace it in the production environment if you intend to actually use this. For Heroku, we will still have to tweak this further, so keep this in mind.</p>
<p>Just to make the process easier, I also added the following helper function:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># lib/ex_pusher_lite.ex</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Return this applicaton administration Basic HTTP Auth hash</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">admin_secret</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">admin_username</span> <span class="o">=</span> <span class="no">Application</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="ss">:admin_authentication</span><span class="p">)</span><span class="o">[</span><span class="ss">:username</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">admin_password</span> <span class="o">=</span> <span class="no">Application</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="ss">:admin_authentication</span><span class="p">)</span><span class="o">[</span><span class="ss">:password</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">secret</span> <span class="o">=</span> <span class="no">Base</span><span class="o">.</span><span class="n">encode64</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="n">admin_username</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">admin_password</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is how you fetch the configuration values. I am generating a simple Base64 encoded string out of the username concatenated with the password with a comma, which is what Basic HTTP Auth requires. I will use this admin hash for the &ldquo;AppController&rdquo; and each client must provide the key/secret in its own App instance to be able to trigger the &ldquo;EventsController&rdquo;.</p>
<p>For both controllers I will create a single Authentication Plug, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># lib/ex_pusher_lite/authentication.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Authentication</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">import</span> <span class="no">Plug</span><span class="o">.</span><span class="n">Conn</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">App</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">assigns</span> <span class="p">\\</span> <span class="o">[</span><span class="ss">admin</span><span class="p">:</span> <span class="kp">false</span><span class="o">]</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="n">assigns</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">assigns</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">token</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">assigns</span><span class="o">[</span><span class="ss">:admin</span><span class="o">]</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">admin_secret</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="n">params</span> <span class="o">=</span> <span class="n">fetch_query_params</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span><span class="o">.</span><span class="n">params</span>
</span></span><span class="line"><span class="cl">        <span class="n">params</span><span class="o">[</span><span class="s2">&#34;app_slug&#34;</span><span class="o">]</span> <span class="o">|&gt;</span> <span class="no">App</span><span class="o">.</span><span class="n">get_by_slug</span> <span class="o">|&gt;</span> <span class="no">App</span><span class="o">.</span><span class="n">hashed_secret</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Basic &#34;</span> <span class="o">&lt;&gt;</span> <span class="n">auth_token</span> <span class="o">=</span> <span class="n">hd</span><span class="p">(</span><span class="n">get_req_header</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="s2">&#34;authorization&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="no">Plug</span><span class="o">.</span><span class="n">Crypto</span><span class="o">.</span><span class="n">secure_compare</span><span class="p">(</span><span class="n">auth_token</span><span class="p">,</span> <span class="n">token</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span> <span class="o">|&gt;</span> <span class="n">send_resp</span><span class="p">(</span><span class="mi">401</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="n">halt</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As I explained in previous articles, a Plug is like a chainable Rails Middleware or even a Rack application. It must have a single &ldquo;<tt>call/2</tt>&rdquo; that receives a Plug.Conn structure and returns it back, allowing to form a chain/pipeline of Plugs.</p>
<p>We check if we want to compare with the Admin token or the App token and then retrieve the Basic HTTP authorization token that&rsquo;s in the HTTP request connection structure (we retrieve individual header values through the &ldquo;<tt>get_req_header/2</tt>&rdquo; function). Finally we make a secure compare between the tokens.</p>
<p>To enable this plug in the controllers we just add it like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/controllers/app_controller.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">AppController</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:controller</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">App</span>
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Authentication</span><span class="p">,</span> <span class="o">[</span><span class="ss">admin</span><span class="p">:</span> <span class="kp">true</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"> <span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">EventsController</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">   <span class="n">use</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:controller</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">-</span>  <span class="n">plug</span> <span class="ss">:authenticate</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>  <span class="n">plug</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Authentication</span>
</span></span><span class="line"><span class="cl">   <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In Part 1 we had a simpler &ldquo;<tt>plug :authenticate</tt>&rdquo; in the EventsController. We can remove it and also the &ldquo;<tt>authenticate/2</tt>&rdquo; function. We just refactored it into a better function that also serves administration authentication now, but the idea is the same.</p>
<p>This is it: the basics for API authentication. Again, this is not the best solution as the username/password pair goes in the URL and it&rsquo;s open to man-in-the-middle attacks. SSL only encrypts the HTTP body but the URL is still open.</p>
<p>For example, if an administrator wants to create a new application, he must do the following:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>curl --data &#34;app[name]=foo-app&#34; http://pusher_admin_username:pusher_admin_password@localhost:4000/api/admin/apps</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this would be one example of the resulting JSON representation of the new app:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>{&#34;data&#34;:{&#34;slug&#34;:&#34;foo-app&#34;,&#34;secret&#34;:&#34;8ef69064-0d7e-c9ef-ac14-b6b1db303e7a&#34;,&#34;name&#34;:&#34;foo-app&#34;,&#34;key&#34;:&#34;9400ad21-eed8-117a-bce5-845262e0a09e&#34;,&#34;id&#34;:5,&#34;active&#34;:true}}%</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>With this new key and secret in hand, we can update our client demo to make use of the new app.</p>
<h3>Configuring the Client Demo<span class="hx:absolute hx:-mt-20" id="configuring-the-client-demo"></span>
    <a href="#configuring-the-client-demo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>We must start by adding the proper Application details in the &ldquo;<tt>.env</tt>&rdquo; file:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>PUSHER_URL: &#34;localhost:4000&#34;
PUSHER_APP_ID: &#34;foo-app&#34;
PUSHER_KEY: &#34;9400ad21-eed8-117a-bce5-845262e0a09e&#34;
PUSHER_SECRET: &#34;8ef69064-0d7e-c9ef-ac14-b6b1db303e7a&#34;
PUSHER_CHANNEL: &#34;foo-topic&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We must also tweak the &ldquo;<tt>config/secrets.yml</tt>&rdquo; to reflect the new metadata (development, test, and production must follow this):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>development:
  secret_key_base: ded7c4a2a298c1b620e462b50c9ca6ccb60130e27968357e76cab73de9858f14556a26df885c8aa5004d0a7ca79c0438e618557275bdb28ba67a0ffb0c268056
  pusher_url: &lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;
  pusher_app_id: &lt;%= ENV[&#39;PUSHER_APP_ID&#39;] %&gt;
  pusher_key: &lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;
  pusher_secret: &lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;
  pusher_channel: &lt;%= ENV[&#39;PUSHER_CHANNEL&#39;] %&gt;
  ...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And we can create an initializer to make it easier to use this metadata properly:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/initializers/pusher_lite.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">PusherLite</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">uri</span>
</span></span><span class="line"><span class="cl">    <span class="n">key</span>    <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_key</span>
</span></span><span class="line"><span class="cl">    <span class="n">secret</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_secret</span>
</span></span><span class="line"><span class="cl">    <span class="n">app_id</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_app_id</span>
</span></span><span class="line"><span class="cl">    <span class="n">url</span>    <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_url</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">uri</span> <span class="o">=</span> <span class="s2">&#34;http://</span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">secret</span><span class="si">}</span><span class="s2">@</span><span class="si">#{</span><span class="n">url</span><span class="si">}</span><span class="s2">/api/apps/</span><span class="si">#{</span><span class="n">app_id</span><span class="si">}</span><span class="s2">/events&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Again, the Rails app will trigger the ExPusherLite server using the Basic HTTP Auth. Do not be fooled into thinking this is &ldquo;secure&rdquo;, it just &ldquo;feels a bit secure through obscurity&rdquo;. You have been warned, wait for the next articles on this subject. But this is usable in controlled environments.</p>
<p>To finalize the upgrades, we must change the client-side access to the new metadata, first changing the application layout:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- app/views/layouts/application.html.erb --&gt;</span>
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">+  <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;pusher_host&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;&lt;%= Rails.application.secrets.pusher_url %&gt;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">-  <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;pusher_key&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;&lt;%= Rails.application.secrets.pusher_key %&gt;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">+  <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;pusher_app_id&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;&lt;%= Rails.application.secrets.pusher_app_id %&gt;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">   <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;pusher_channel&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;&lt;%= Rails.application.secrets.pusher_channel %&gt;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">...</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The javascript &ldquo;<tt>index.es6</tt>&rdquo; fetches from this meta headers, so we must change them there:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="err">#</span> <span class="nx">app</span><span class="o">/</span><span class="nx">assets</span><span class="o">/</span><span class="nx">javascripts</span><span class="o">/</span><span class="nx">application</span><span class="o">/</span><span class="nx">pages</span><span class="o">/</span><span class="nx">home</span><span class="o">/</span><span class="nx">index</span><span class="p">.</span><span class="nx">es6</span>
</span></span><span class="line"><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="cl">     <span class="kd">let</span> <span class="nx">guardianToken</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=guardian-token]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="kd">let</span> <span class="nx">csrfToken</span>     <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=guardian-csrf]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="kd">let</span> <span class="nx">pusherHost</span>    <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_host]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="kd">let</span> <span class="nx">pusherKey</span>     <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_key]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="kd">let</span> <span class="nx">pusherApp</span>     <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_app_id]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="kd">let</span> <span class="nx">pusherChannel</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_channel]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="kd">let</span> <span class="nx">socket</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Socket</span><span class="p">(</span><span class="s2">&#34;ws://localhost:4000/socket&#34;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="kd">let</span> <span class="nx">socket</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Socket</span><span class="p">(</span><span class="sb">`ws://</span><span class="si">${</span><span class="nx">pusherHost</span><span class="si">}</span><span class="sb">/socket`</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">       <span class="nx">params</span><span class="o">:</span> <span class="p">{</span> <span class="nx">guardian_token</span><span class="o">:</span> <span class="nx">guardianToken</span><span class="p">,</span> <span class="nx">csrf_token</span><span class="o">:</span> <span class="nx">csrfToken</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">     <span class="p">})</span>
</span></span><span class="line"><span class="cl">     <span class="nx">socket</span><span class="p">.</span><span class="nx">connect</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">     <span class="c1">// Now that you are connected, you can join channels with a topic:
</span></span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="kd">let</span> <span class="nx">channel</span> <span class="o">=</span> <span class="nx">socket</span><span class="p">.</span><span class="nx">channel</span><span class="p">(</span><span class="nx">pusherChannel</span><span class="p">,</span> <span class="p">{})</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="kd">let</span> <span class="nx">channel</span> <span class="o">=</span> <span class="nx">socket</span><span class="p">.</span><span class="nx">channel</span><span class="p">(</span><span class="sb">`public:</span><span class="si">${</span><span class="nx">pusherApp</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="p">{})</span>
</span></span><span class="line"><span class="cl">     <span class="nx">channel</span><span class="p">.</span><span class="nx">join</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">       <span class="p">.</span><span class="nx">receive</span><span class="p">(</span><span class="s2">&#34;ok&#34;</span><span class="p">,</span> <span class="nx">resp</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Joined successfully&#34;</span><span class="p">,</span> <span class="nx">resp</span><span class="p">)</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">       <span class="p">.</span><span class="nx">receive</span><span class="p">(</span><span class="s2">&#34;error&#34;</span><span class="p">,</span> <span class="nx">resp</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Unable to join&#34;</span><span class="p">,</span> <span class="nx">resp</span><span class="p">)</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="nx">channel</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="nx">data</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="nx">channel</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">pusherChannel</span><span class="si">}</span><span class="sb">:msg`</span><span class="p">,</span> <span class="nx">data</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">       <span class="kd">let</span> <span class="nx">new_line</span> <span class="o">=</span> <span class="sb">`&lt;p&gt;&lt;strong&gt;</span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">&lt;strong&gt;: </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">message</span><span class="si">}</span><span class="sb">&lt;/p&gt;`</span>
</span></span><span class="line"><span class="cl">       <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;.message-receiver&#34;</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">new_line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="nx">channel</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="nx">data</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>      <span class="kd">let</span> <span class="nx">new_line</span> <span class="o">=</span> <span class="sb">`&lt;p&gt;&lt;strong&gt;Broadcast to all channels&lt;/strong&gt;: </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">message</span><span class="si">}</span><span class="sb">&lt;/p&gt;`</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>      <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;.message-receiver&#34;</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">new_line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="p">})</span>
</span></span><span class="line"><span class="cl">   <span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>One important modification from Part 1 is that the WebSocket host was hardcoded to &ldquo;localhost&rdquo; and here we are making it configurable through the meta tags. Right now, for localhost tests, we are using the plain &ldquo;ws://&rdquo; protocol but when we deploy to Heroku we will change it to &ldquo;wss://&rdquo; for SSL. Same thing for the &ldquo;PusherLite&rdquo; initializer. Keep that in mind.</p>
<p>Now it&rsquo;s subscribing to a different format of topic/channel. In Part 1 it would be something like: &ldquo;<tt>public:test_channel</tt>&rdquo; now we are listening to &ldquo;<tt>public:foo-app</tt>&rdquo;, so the application is the Websocket subscription &ldquo;topic&rdquo;.</p>
<p>Then we are changing the socket listener to listen for 2 different events. The first one is in the format &ldquo;<tt>test_channel:msg</tt>&rdquo;. So this is how we must now send messages to a specific &ldquo;channel&rdquo; within an &ldquo;app/topic&rdquo;.</p>
<p>And last we still listen to the old &ldquo;msg&rdquo; event, but this serves as a &ldquo;broadcast&rdquo; event for all connected clients subscribed to this particular &ldquo;foo-app&rdquo; Application. Now web clients can listen to specific &ldquo;channels&rdquo; within the &ldquo;app&rdquo; but also receive system wide &ldquo;broadcast&rdquo; messages. This is a big improvement and it didn&rsquo;t require much on the Javascript side.</p>
<p>But what more does it take to make this <strong>&ldquo;channel-only and broadcast&rdquo;</strong> system work? First, we start changing the web form to allow a user to choose between sending a channel-only message or a broadcast, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- app/views/home/index.html.erb --&gt;</span>
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">     <span class="err">&lt;</span>%= f.text_field :name, placeholder: &#34;Name&#34; %&gt;
</span></span><span class="line"><span class="cl">     <span class="err">&lt;</span>%= f.text_field :message, placeholder: &#34;Message&#34; %&gt;
</span></span><span class="line"><span class="cl">+    <span class="err">&lt;</span>%= f.check_box :broadcast %&gt;
</span></span><span class="line"><span class="cl">     <span class="err">&lt;</span>%= f.submit &#34;Send message&#34;, class: &#34;pure-button pure-button-primary&#34; %&gt;
</span></span><span class="line"><span class="cl">   <span class="p">&lt;/</span><span class="nt">fieldset</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">...</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now the EventsController must accept this new parameter:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/controllers/events_controller.rb</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">event_params</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:pusher_event</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span><span class="p">,</span> <span class="ss">:broadcast</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Finally, the Model must use this new information before posting to the ExPusherLite server:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/models/pusher_event.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PusherEvent</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">ActiveModel</span><span class="o">::</span><span class="no">Model</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span><span class="p">,</span> <span class="ss">:broadcast</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">save</span>
</span></span><span class="line"><span class="cl">    <span class="n">topic</span> <span class="o">=</span> <span class="k">if</span> <span class="n">broadcast</span> <span class="o">==</span> <span class="s2">&#34;1&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="s2">&#34;#general&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">else</span>
</span></span><span class="line"><span class="cl">              <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_channel</span>
</span></span><span class="line"><span class="cl">            <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">post_form</span><span class="p">(</span><span class="no">PusherLite</span><span class="o">.</span><span class="n">uri</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;topic&#34;</span> <span class="o">=&gt;</span> <span class="n">topic</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;event&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;msg&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;scope&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;public&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;payload&#34;</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span> <span class="o">=&gt;</span> <span class="nb">name</span><span class="p">,</span> <span class="s2">&#34;message&#34;</span> <span class="o">=&gt;</span> <span class="n">message</span><span class="p">}</span><span class="o">.</span><span class="n">to_json</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I am just assuming a hard-coded &ldquo;<tt>#general</tt>&rdquo; string to serve as the broadcast trigger for the server. Now we must make the server accept this new protocol schema, so let&rsquo;s go back to Elixir.</p>
<p>First we must start with the counterpart for the previous POST trigger, <tt>ExPusherLite.EventsController</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/controllers/events_controller.ex</span>
</span></span><span class="line"><span class="cl"> <span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">EventsController</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">   <span class="n">use</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:controller</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">-</span>  <span class="n">plug</span> <span class="ss">:authenticate</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>  <span class="n">plug</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Authentication</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">-</span>  <span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="n">topic</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="s2">&#34;topic&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="n">event</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="s2">&#34;event&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>  <span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="sx">%{&#34;app_slug&#34; =&gt; app_slug, &#34;event&#34; =&gt; event, &#34;topic&#34; =&gt; topic, &#34;scope&#34; =&gt; scope}</span> <span class="o">=</span> <span class="n">params</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">     <span class="n">message</span> <span class="o">=</span> <span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="s2">&#34;payload&#34;</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&#34;{}&#34;</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="no">Poison</span><span class="o">.</span><span class="n">decode!</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Endpoint</span><span class="o">.</span><span class="n">broadcast!</span> <span class="n">topic</span><span class="p">,</span> <span class="n">event</span><span class="p">,</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">topic_event</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>      <span class="k">if</span> <span class="n">topic</span> <span class="o">==</span> <span class="s2">&#34;#general&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="n">event</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>      <span class="k">else</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="s2">&#34;</span><span class="si">#{</span><span class="n">topic</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">event</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>      <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Endpoint</span><span class="o">.</span><span class="n">broadcast!</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">scope</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">app_slug</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span> <span class="n">topic_event</span><span class="p">,</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">     <span class="n">json</span> <span class="n">conn</span><span class="p">,</span> <span class="sx">%{}</span>
</span></span><span class="line"><span class="cl">   <span class="k">end</span>
</span></span><span class="line"><span class="cl">   <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The first difference is that I am pattern matching from the arguments directly to the &ldquo;topic&rdquo; and &ldquo;event&rdquo; variables. This function is also aware of the &ldquo;<tt>#general</tt>&rdquo; string the client can send to indicate an app-wide broadcast. And the new topic is the concatenation of &ldquo;topic&rdquo; and &ldquo;event&rdquo; to allow for &ldquo;channel-only&rdquo; messages.</p>
<p>To connect this all to the WebSocket handler, we must make the following changes:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/channels/room_channel.ex</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>  <span class="k">def</span> <span class="nf">handle_in</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">socket</span> <span class="o">=</span> <span class="sx">%{ topic: &#34;public:&#34; &lt;&gt; _ }</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="n">broadcast</span> <span class="n">socket</span><span class="p">,</span> <span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>  <span class="k">def</span> <span class="nf">handle_in</span><span class="p">(</span><span class="n">topic_event</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">socket</span> <span class="o">=</span> <span class="sx">%{ topic: &#34;public:&#34; &lt;&gt; _ }</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">broadcast</span> <span class="n">socket</span><span class="p">,</span> <span class="n">topic_event</span><span class="p">,</span> <span class="n">payload</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span> <span class="ss">:noreply</span><span class="p">,</span> <span class="n">socket</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">   <span class="k">end</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">-</span>  <span class="k">def</span> <span class="nf">handle_in</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>  <span class="k">def</span> <span class="nf">handle_in</span><span class="p">(</span><span class="n">topic_event</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">     <span class="n">claims</span> <span class="o">=</span> <span class="no">Guardian</span><span class="o">.</span><span class="n">Channel</span><span class="o">.</span><span class="n">claims</span><span class="p">(</span><span class="n">socket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="k">if</span> <span class="n">permitted_topic?</span><span class="p">(</span><span class="n">claims</span><span class="o">[</span><span class="ss">:publish</span><span class="o">]</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">topic</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>      <span class="n">broadcast</span> <span class="n">socket</span><span class="p">,</span> <span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>      <span class="n">broadcast</span> <span class="n">socket</span><span class="p">,</span> <span class="n">topic_event</span><span class="p">,</span> <span class="n">payload</span>
</span></span><span class="line"><span class="cl">       <span class="p">{</span> <span class="ss">:noreply</span><span class="p">,</span> <span class="n">socket</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">   <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, the Channel does not pattern match on a specific event, it lets it through without further validation, trusting that the EventsController is doing the right thing. I will come back to this piece for improvements in the future, possibly.</p>
<h3>Deploying our first Phoenix app to Heroku!<span class="hx:absolute hx:-mt-20" id="deploying-our-first-phoenix-app-to-heroku"></span>
    <a href="#deploying-our-first-phoenix-app-to-heroku" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In this section we will just follow the <a href="http://www.phoenixframework.org/docs/heroku"target="_blank" rel="noopener">official documentation</a>, so read it if you want more details.</p>
<p>Let&rsquo;s get started:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>heroku apps:create your-expusherlite --buildpack &#34;https://github.com/HashNuke/heroku-buildpack-elixir.git&#34;
heroku buildpacks:add https://github.com/gjaldon/heroku-buildpack-phoenix-static.git</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I am naming the application &ldquo;your-expusherlite&rdquo; but you should change it to your own name, of course. And the rest of the configuration data are all examples that you must change for your own needs.</p>
<p>Heroku relies on environment variables. So we start by erasing &ldquo;<tt>config/prod.secret.exs</tt>&rdquo; and change &ldquo;<tt>config/prod.exs</tt>&rdquo; to look like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Endpoint</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">http</span><span class="p">:</span> <span class="o">[</span><span class="ss">port</span><span class="p">:</span> <span class="p">{</span><span class="ss">:system</span><span class="p">,</span> <span class="s2">&#34;PORT&#34;</span><span class="p">}</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">url</span><span class="p">:</span> <span class="o">[</span><span class="ss">scheme</span><span class="p">:</span> <span class="s2">&#34;https&#34;</span><span class="p">,</span> <span class="ss">host</span><span class="p">:</span> <span class="s2">&#34;your-expusherlite.herokuapp.com&#34;</span><span class="p">,</span> <span class="ss">port</span><span class="p">:</span> <span class="mi">443</span><span class="o">]</span><span class="p">,</span> <span class="ss">force_ssl</span><span class="p">:</span> <span class="o">[</span><span class="ss">rewrite_on</span><span class="p">:</span> <span class="o">[</span><span class="ss">:x_forwarded_proto</span><span class="o">]]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">cache_static_manifest</span><span class="p">:</span> <span class="s2">&#34;priv/static/manifest.json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">secret_key_base</span><span class="p">:</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;SECRET_KEY_BASE&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Configure your database</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Repo</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">adapter</span><span class="p">:</span> <span class="no">Ecto</span><span class="o">.</span><span class="n">Adapters</span><span class="o">.</span><span class="n">Postgres</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">url</span><span class="p">:</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;DATABASE_URL&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="ss">pool_size</span><span class="p">:</span> <span class="mi">20</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="ss">:admin_authentication</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">username</span><span class="p">:</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;PUSHER_ADMIN_USERNAME&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="ss">password</span><span class="p">:</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;PUSHER_ADMIN_PASSWORD&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># remove this line:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># import_config &#34;prod.secret.exs&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now we must configure the environment variables &ldquo;SECRET_KEY_BASE&rdquo;, &ldquo;PUSHER_ADMIN_USERNAME&rdquo; and &ldquo;PUSHER_ADMIN_PASSWORD&rdquo;. Use the included &ldquo;<tt>mix phoenix.gen.secret</tt>&rdquo; to generate those.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>heroku config:set SECRET_KEY_BASE=&#34;`mix phoenix.gen.secret`&#34;
heroku config:set PUSHER_ADMIN_USERNAME=&#34;FPO0QUkqbAP6EGjElqBzDQuMs8bhFS3&#34;
heroku config:set PUSHER_ADMIN_PASSWORD=&#34;n78DPGmK3DBQy8YAVyshiGqcXjjSXSD&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Then it&rsquo;s just a matter of waiting for the good old &ldquo;<tt>git push heroku master</tt>&rdquo; to finish compiling everything for the first time. And because this is the first deploy you should not forget to run &ldquo;<tt>heroku run mix ecto.migrate</tt>&rdquo; to create the database table.</p>
<p>Now, if I did everything right, as an Administrator that knows the above hardcoded secrets I should be able to create a new Application like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>curl --data &#34;app[name]=shiny-new-app&#34; https://FPO0QUkqbAP6EGjElqBzDQuMs8bhFS3:n78DPGmK3DBQy8YAVyshiGqcXjjSXSD@your-expusherlite.herokuapp.com/api/admin/apps</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is the result I got!</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>{&#34;data&#34;:{&#34;slug&#34;:&#34;shiny-new-app&#34;,&#34;secret&#34;:&#34;42560373-0fe1-506e-28ca-35ab5221fb3d&#34;,&#34;name&#34;:&#34;shiny-new-app&#34;,&#34;key&#34;:&#34;958c16e7-ab93-dac0-0fc6-6cb864e26358&#34;,&#34;id&#34;:1,&#34;active&#34;:true}}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Great, now that we have a valid Application key and secret we can configure our Rails Client Demo and deploy it to Heroku as well.</p>
<h3>Deploying the Rails Client to Heroku<span class="hx:absolute hx:-mt-20" id="deploying-the-rails-client-to-heroku"></span>
    <a href="#deploying-the-rails-client-to-heroku" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>This is a simple Rails application, we can just create the app and deploy right away:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>heroku create your-expusherlite-client
heroku config:set PUSHER_URL=your-expusherlite.herokuapp.com
heroku config:set PUSHER_APP_ID=shiny-new-app
heroku config:set PUSHER_KEY=958c16e7-ab93-dac0-0fc6-6cb864e26358
heroku config:set PUSHER_SECRET=42560373-0fe1-506e-28ca-35ab5221fb3d
heroku config:set PUSHER_CHANNEL=shiny-new-topic
git push heroku master</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I&rsquo;m assuming the readers of this post already know how to configure a Rails app properly for Heroku. Just to mention it, I configure this app with the 12 factor and Puma gems and added a proper Procfile. Another very small change was changing the &ldquo;pusher_lite.rb&rdquo; initializer to create a URI with &ldquo;https&rdquo; because the ExPusherLite we deployed to production requires SSL by default.</p>
<p>There is one more caveat. Being led by experienced web programmers, they made sure that, unlike this bare-bones exercise here, the Phoenix framework itself is secure. One such example is to disallow Websocket connections from different hosts.</p>
<p>Out of the box, the &ldquo;phoenix.js&rdquo; Socket will fail connection when we try to connect from the &ldquo;your-expusherlite-client.herokuapp.com&rdquo; Rails app host to the Phoenix app in &ldquo;your-expusherlite.herokuapp.com&rdquo; with the following error:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>WebSocket connection to &#39;wss://your-expusherlite.herokuapp.com/socket/websocket?guardian_token=N_YCG6hGK7…iOlsicHVibGljOioiXX0._j6s2LiaKde9rBhnTMxDkm0XV5u89pNh1AdLFY6Rlt8&amp;vsn=1.0.0&#39; failed: Error during WebSocket handshake: Unexpected response code: 403</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And in the Phoenix log we will see this very helpful message:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[error] Could not check origin for Phoenix.Socket transport.

This happens when you are attempting a socket connection to
a different host than the one configured in your config/
files. For example, in development the host is configured
to &#34;localhost&#34; but you may be trying to access it from
&#34;127.0.0.1&#34;. To fix this issue, you may either:

  1. update [url: [host: ...]] to your actual host in the
     config file for your current environment (recommended)

  2. pass the :check_origin option when configuring your
     endpoint or when configuring the transport in your
     UserSocket module, explicitly outlining which origins
     are allowed:

        check_origin: [&#34;https://example.com&#34;,
                       &#34;//another.com:888&#34;, &#34;//other.com&#34;]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Unless you know what a <a href="https://www.christian-schneider.net/CrossSiteWebSocketHijacking.html"target="_blank" rel="noopener">Cross-Site Web Socket Hijacking</a> is, you will prefer to keep the default settings as they are. In a green-field Phoenix app, the Web part will connect to the Web Socket in the same app and, therefore, in the same host, so this is not an issue.</p>
<p>In this case I am making a separate micro-service to mimic Pusher.com behavior so it should be able to accept Web Socket connections from different hosts.</p>
<p>If you control the applications being created, you will likely prefer to make the &ldquo;<tt>check_origin</tt>&rdquo; setting read from your database for the exact hosts. As a feature for next time I could add a &ldquo;host&rdquo; field in the &ldquo;App&rdquo; model and use it to validate connections in the transport configuration. For the time being I will just make it accept any hosts:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># web/channels/user_socket.ex
defmodule ExPusherLite.UserSocket do
  use Phoenix.Socket

  ## Channels
  channel &#34;*&#34;, ExPusherLite.RoomChannel

  ## Transports
  transport :websocket, Phoenix.Transports.WebSocket, check_origin: false
  ...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is it! Now the Rails app should be able to connect and send messages! And you should be able to create any number of new apps and connect all of them to this same service.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/534/big_Screenshot_from_2015-12-14_14_58_22.png" alt="Final Heroku Client"  loading="lazy" /></p>
<h3>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Right now, we have a functional, albeit bare-bones, Pusher.com clone that will work for any number of use cases where Pusher.com would be used.</p>
<p>As I warned many times before, the security part is still flaky and needs work. I will still extend on what Daniel began with Guardian to authenticate Web Socket users to private channels as well. And the core should also receive auditing and reporting capabilities (to be able to report usage, number of active connections, throughput of events, keep at least a short history of events so new connections can retrieve the last sent messages, and so on).</p>
<p>But from here it&rsquo;s a matter of adding features to an already working core. And this is nothing more than Phoenix out-of-the-box without too much added on top of it! It says a lot about the current state of maturity of this very capable framework.</p>
<p>In terms of performance, for this very simple example, I spun up 1 free Heroku dyno for each app.</p>
<p>The Rails app is able to respond to the front-end user interface in around 2ms. And the Sucker Punch job - which does the heavy HTTP POST to ExPusherLite - takes in the order of 30ms or less.</p>
<p>The Phoenix server receives the HTTP POST and performs the broadcast in around less than 6ms. Also quite fast. The times will vary a lot because I believe the free dyno is not only slow but also stays in highly shared metal boxes, getting impact from other neighbor apps running in the same box.</p>
<p>Because we already have an administrative API to create and manage apps (create new ones, delete, update, etc) we can already create a separate application in any other framework to make a dashboard for admins or for a self-serving front-end for developers to register new apps and receive key/secret pair to add in their own applications.</p>
<p>Both the ExPusherLite server and the demo client are deployed in Heroku and you can test the client right now <a href="https://expusherlite-client.herokuapp.com/"target="_blank" rel="noopener">clicking here</a>. The admin keys are different from what I showed in this post, of course, so you won&rsquo;t be able to create new apps, but you can deploy it yourself to your own environment.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>devops</category></item><item><title>Ex Pusher Lite - Part 1: Phoenix Channels and Rails apps</title><link>https://akitaonrails.github.io/en/2015/12/09/ex-pusher-lite-part-1-phoenix-channels-and-rails-apps/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/09/ex-pusher-lite-part-1-phoenix-channels-and-rails-apps/</guid><pubDate>Wed, 09 Dec 2015 18:39:00 -0200</pubDate><description>&lt;p&gt;Finally, after a lengthy exercising period (and plenty of blogging!) I will start implementing the Elixir app I wanted from the very beginning.&lt;/p&gt;
&lt;p&gt;As a Rails developer there are a few things we can&amp;rsquo;t do in Rails. One of them is to deal with real-time messaging.&lt;/p&gt;
&lt;p&gt;Rails 5 will bring &lt;a href="https://github.com/rails/actioncable"target="_blank" rel="noopener"&gt;Action Cable&lt;/a&gt;, and it might be good enough for most cases. It uses &lt;a href="https://github.com/faye/faye"target="_blank" rel="noopener"&gt;Faye&lt;/a&gt;, which in turn is based on Eventmachine. You can implement a good enough solution for Websockets using Faye in your Rails 4.2 app right now.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Finally, after a lengthy exercising period (and plenty of blogging!) I will start implementing the Elixir app I wanted from the very beginning.</p>
<p>As a Rails developer there are a few things we can&rsquo;t do in Rails. One of them is to deal with real-time messaging.</p>
<p>Rails 5 will bring <a href="https://github.com/rails/actioncable"target="_blank" rel="noopener">Action Cable</a>, and it might be good enough for most cases. It uses <a href="https://github.com/faye/faye"target="_blank" rel="noopener">Faye</a>, which in turn is based on Eventmachine. You can implement a good enough solution for Websockets using Faye in your Rails 4.2 app right now.</p>
<p>Another option is to avoid the trouble altogether and use a messaging service. One option I always recommend for zero friction is to use <a href="https://pusher.com/"target="_blank" rel="noopener">Pusher.com</a>.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/533/big_Screenshot_from_2015-12-09_18_41_22.png" alt="Chat Example"  loading="lazy" /></p>
<h3>The Basics<span class="hx:absolute hx:-mt-20" id="the-basics"></span>
    <a href="#the-basics" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>You will want to clone from my <a href="https://github.com/akitaonrails/pusher_lite_demo/tree/v0.1"target="_blank" rel="noopener">example repository</a>, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>git clone https://github.com/akitaonrails/pusher_lite_demo
cd pusher_lite_demo
git checkout tags/v0.1 -b v0.1
bundle</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is a very very simple implementation of a real-time, websocket-based, chat using Pusher. The idea goes like this:</p>
<p>We start by having a front-end Form to send messages</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- app/views/home/index.html.erb --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= form_for @event, url: events_path, remote: true, html: {class: &#34;pure-form pure-form-stacked&#34;} do |f| %&gt;
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">fieldset</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">legend</span><span class="p">&gt;</span>Send your message remotely<span class="p">&lt;/</span><span class="nt">legend</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;</span>%= f.text_field :name, placeholder: &#34;Name&#34; %&gt;
</span></span><span class="line"><span class="cl">    <span class="err">&lt;</span>%= f.text_field :message, placeholder: &#34;Message&#34; %&gt;
</span></span><span class="line"><span class="cl">    <span class="err">&lt;</span>%= f.submit &#34;Send message&#34;, class: &#34;pure-button pure-button-primary&#34; %&gt;
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">fieldset</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>% end %&gt;</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s using Rails built-in jQuery support for Ajax posting the form to the &ldquo;EventsController#create&rdquo; method:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/controllers/events_controller.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">EventsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="no">SendEventsJob</span><span class="o">.</span><span class="n">perform_later</span><span class="p">(</span><span class="n">event_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">event_params</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:pusher_event</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Just to annotate the process, the &ldquo;routes.rb&rdquo; looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/routes.rb</span>
</span></span><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:events</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:create</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">root</span> <span class="s1">&#39;home#index&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The HTML layout looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">&lt;!--</span> <span class="n">app</span><span class="o">/</span><span class="n">views</span><span class="o">/</span><span class="n">layout</span><span class="o">/</span><span class="n">application</span><span class="o">.</span><span class="n">html</span><span class="o">.</span><span class="n">erb</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;!</span><span class="no">DOCTYPE</span> <span class="n">html</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="n">html</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="n">head</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="n">title</span><span class="o">&gt;</span><span class="no">Pusher</span> <span class="no">Lite</span> <span class="no">Demo</span><span class="o">&lt;</span><span class="sr">/title&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">  &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">  &lt;meta name=&#34;pusher_key&#34; content=&#34;&lt;%= Rails.application.secrets.pusher_key %&gt;&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">  &lt;meta name=&#34;pusher_channel&#34; content=&#34;&lt;%= Rails.application.secrets.pusher_channel %&gt;&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">  &lt;%= stylesheet_link_tag    &#39;application&#39;, media: &#39;all&#39;, &#39;data-turbolinks-track&#39; =&gt; true %&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">  &lt;%= javascript_include_tag &#39;application&#39;, &#39;data-turbolinks-track&#39; =&gt; true %&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">  &lt;script src=&#34;https://akitaonrails.github.io/</span><span class="o">/</span><span class="n">js</span><span class="o">.</span><span class="n">pusher</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="mi">3</span><span class="o">.</span><span class="mi">0</span><span class="o">/</span><span class="n">pusher</span><span class="o">.</span><span class="n">min</span><span class="o">.</span><span class="n">js</span><span class="s2">&#34;&gt;&lt;/script&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">  &lt;%= csrf_meta_tags %&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;/head&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;body&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;div class=&#34;</span><span class="n">pure</span><span class="o">-</span><span class="n">menu</span> <span class="n">pure</span><span class="o">-</span><span class="n">menu</span><span class="o">-</span><span class="n">horizontal</span><span class="s2">&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Pusher Client Demo
</span></span></span><span class="line"><span class="cl"><span class="s2">...
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;div class=&#34;</span><span class="n">pure</span><span class="o">-</span><span class="n">g</span><span class="o">-</span><span class="n">r</span><span class="s2">&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">  &lt;div class=&#34;</span><span class="n">pure</span><span class="o">-</span><span class="n">u</span><span class="o">-</span><span class="mi">1</span><span class="o">-</span><span class="mi">3</span> <span class="n">message</span><span class="o">-</span><span class="n">form</span><span class="s2">&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">    &lt;%= yield %&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">  &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">  
</span></span></span><span class="line"><span class="cl"><span class="s2">  &lt;div class=&#34;</span><span class="n">pure</span><span class="o">-</span><span class="n">u</span><span class="o">-</span><span class="mi">1</span><span class="o">-</span><span class="mi">3</span> <span class="n">message</span><span class="o">-</span><span class="n">receiver</span><span class="s2">&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">  &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;/body&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;/html&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This layout imports the default &ldquo;application.js&rdquo; which configures Pusher, establishes the Websocket connection and subscribes to messages on a specific topic with specific events:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// app/assets/javascript/application.js
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require jquery
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require jquery_ujs
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require turbolinks
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require_tree .
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">on</span><span class="p">(</span><span class="s2">&#34;page:change&#34;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">pusherKey</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_key]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">pusher</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Pusher</span><span class="p">(</span><span class="nx">pusherKey</span><span class="p">,</span> <span class="p">{</span> <span class="nx">encrypted</span><span class="o">:</span> <span class="kc">true</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">pusherChannel</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_channel]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">channel</span> <span class="o">=</span> <span class="nx">pusher</span><span class="p">.</span><span class="nx">subscribe</span><span class="p">(</span><span class="nx">pusherChannel</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="nx">channel</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="s1">&#39;new_message&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">new_line</span> <span class="o">=</span> <span class="s2">&#34;&lt;p&gt;&lt;strong&gt;&#34;</span> <span class="o">+</span> <span class="nx">data</span><span class="p">.</span><span class="nx">name</span> <span class="o">+</span> <span class="s2">&#34;&lt;strong&gt;: &#34;</span> <span class="o">+</span> <span class="nx">data</span><span class="p">.</span><span class="nx">message</span> <span class="o">+</span> <span class="s2">&#34;&lt;/p&gt;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;.message-receiver&#34;</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">new_line</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It gets the configuration metadata from the layout meta tags which grabs the values from &ldquo;config/secrets.yml&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">development</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">secret_key_base</span><span class="p">:</span><span class="w"> </span><span class="l">ded7c4a2a298c1b620e462b50c9ca6ccb60130e27968357e76cab73de9858f14556a26df885c8aa5004d0a7ca79c0438e618557275bdb28ba67a0ffb0c268056</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_url</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_key</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_channel</span><span class="p">:</span><span class="w"> </span><span class="l">test_chat_channel</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">secret_key_base</span><span class="p">:</span><span class="w"> </span><span class="l">f51ff494801ff0f9e1711036ef6f2f6f1e13544b02326adc5629c6833ae90f1a476747fae94b792eba8a444305df8e7a5ad53f05ea4234692ac96cc44f372029</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_url</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_key</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_channel</span><span class="p">:</span><span class="w"> </span><span class="l">test_chat_channel</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># Do not keep production secrets in the repository,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># instead read values from the environment.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">production</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">secret_key_base</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#34;SECRET_KEY_BASE&#34;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_url</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_key</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_channel</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_CHANNEL&#39;] %&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And as I&rsquo;m using dotenv-rails, the &ldquo;.env&rdquo; looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>PUSHER_URL: &#34;https://14e86e5fee3335fa88b0:2b94ff0f07ce9769567f@api.pusherapp.com/apps/159621&#34;
PUSHER_KEY: &#34;14e86e5fee3335fa88b0&#34;
PUSHER_CHANNEL: &#34;test_chat_channel&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pusher is configured on the server-side through this initializer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/initializers/pusher.rb</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;pusher&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_url</span>
</span></span><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">logger</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">logger</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Finally, the &ldquo;EventsController#create&rdquo; actually does an async call to a <a href="https://github.com/brandonhilkert/sucker_punch"target="_blank" rel="noopener">SuckerPunch</a> job:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SendEventsJob</span> <span class="o">&lt;</span> <span class="no">ActiveJob</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">queue_as</span> <span class="ss">:default</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">perform</span><span class="p">(</span><span class="n">event_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@event</span> <span class="o">=</span> <span class="no">PusherEvent</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">event_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@event</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>By the way, as a segue, <a href="https://github.com/brandonhilkert/sucker_punch"target="_blank" rel="noopener">SuckerPunch</a> is a terrific solution for in-process asynchronous tasks. It&rsquo;s a better option to start with it without having to implement a separate system with Sidekiq workers.</p>
<p>Once you have larger job queues or jobs that are taking too long, then go to Sidekiq. If you use ActiveJob, the transition is as simple as changing the following configuration line in the &ldquo;config/application.rb&rdquo; file:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">config</span><span class="o">.</span><span class="n">active_job</span><span class="o">.</span><span class="n">queue_adapter</span> <span class="o">=</span> <span class="ss">:sucker_punch</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This job just calls the &ldquo;save&rdquo; method in the fake-model &ldquo;PusherEvent&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PusherEvent</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">ActiveModel</span><span class="o">::</span><span class="no">Model</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">save</span>
</span></span><span class="line"><span class="cl">    <span class="no">Pusher</span><span class="o">.</span><span class="n">trigger</span><span class="p">(</span><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_channel</span><span class="p">,</span> <span class="s1">&#39;new_message&#39;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nb">name</span><span class="p">:</span> <span class="nb">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">message</span><span class="p">:</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As it&rsquo;s very simple, the Gemfile is equally simple:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;pusher&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;dotenv-rails&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;purecss-rails&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;sucker_punch&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So what it does is very simple:</p>
<ol>
<li>The application loads Pusher and the necessary configuration.</li>
<li>When the user goes to &ldquo;http://localhost:3000&rdquo; he is presented with the message Form.</li>
<li>When the user posts the message, it ends in &ldquo;EventsController#create&rdquo;, which calls SuckerPunch&rsquo;s &ldquo;SendEventsJob&rdquo;, instantiates a new &ldquo;PusherEvent&rdquo; model with the received form params and finally triggers the message to the Pusher server.</li>
<li>The same form page also loads the Pusher javascript client and connects to the &ldquo;test_chat_channel&rdquo; topic and listens to the &ldquo;new_message&rdquo; event, which is the same thing the &ldquo;Pusher.trigger&rdquo; call sends together with the form message params.</li>
<li>The Pusher server broadcasts the received trigger message to all subscribed Websocket clients.</li>
<li>The Pusher javascript client in the user&rsquo;s browser receives the new message and just formats the message and appends to the &ldquo;message-receiver&rdquo; div HTML block in the same page.</li>
</ol>
<p>Pusher has support for authenticated users, private channels and more, but this is 80% of the usage for most cases. You can implement this as a chat system, a notification system, or anything like that.</p>
<p>Your Rails app sets up the front-end HTML/Javascript to connect to Pusher, listening to certain topics and events and the same Rails app triggers Pusher on the server-side, posting new messages. Pusher receives messages and broadcasts to the clients that subscribe to its topics. That&rsquo;s it.</p>
<h2>Ex Pusher Lite - Part 1: Initial Phoenix-based Pusher replacement<span class="hx:absolute hx:-mt-20" id="ex-pusher-lite---part-1-initial-phoenix-based-pusher-replacement"></span>
    <a href="#ex-pusher-lite---part-1-initial-phoenix-based-pusher-replacement" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>My original idea was to make a drop-in replacement for the Pusher server, using the same Pusher client, but for now it was not easy to do so.</p>
<p>Instead, this Part 1 will focus on implementing an initial ExPusherLite server that also receives events triggered by the same Rails server-side controller process and broadcasting to the same Rails front-end component through WebSockets.</p>
<p>I followed <a href="https://labs.opendoor.com/phoenix-on-rails-for-client-push-notifications"target="_blank" rel="noopener">Daniel Neighman</a>&rsquo;s tutorial. I had to do a few adjustments to have it working (and as this is still Part 1, it&rsquo;s not a complete solution yet!)</p>
<p>You can clone the initial version from <a href="https://github.com/akitaonrails/ex_pusher_lite"target="_blank" rel="noopener">my other Github repository</a> like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>git clone https://github.com/akitaonrails/ex_pusher_lite
cd ex_pusher_lite
mix deps.get</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The tutorial implemented initial setup for <a href="https://github.com/hassox/guardian"target="_blank" rel="noopener">Guardian</a> and <a href="https://github.com/bryanjos/joken"target="_blank" rel="noopener">Joken</a> for JSON Web Tokens. I am still getting used to how <a href="http://www.phoenixframework.org/docs/channels"target="_blank" rel="noopener">channels</a> are implemented in Phoenix.</p>
<p>It already comes pre-configured with a single socket handler that multiplexes connections. You start through the EndPoint OTP application:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># lib/ex_pusher_lite/endpoint.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Endpoint</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Phoenix</span><span class="o">.</span><span class="n">Endpoint</span><span class="p">,</span> <span class="ss">otp_app</span><span class="p">:</span> <span class="ss">:ex_pusher_lite</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">socket</span> <span class="s2">&#34;/socket&#34;</span><span class="p">,</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">UserSocket</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This application is started by the main supervisor in &ldquo;lib/ex_pusher_lite.ex&rdquo;. It points the endpoint &ldquo;/socket&rdquo; to the socket handler &ldquo;UserSocket&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/channels/user_socket.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">UserSocket</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Phoenix</span><span class="o">.</span><span class="n">Socket</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">## Channels</span>
</span></span><span class="line"><span class="cl">  <span class="n">channel</span> <span class="s2">&#34;*&#34;</span><span class="p">,</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">RoomChannel</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">## Transports</span>
</span></span><span class="line"><span class="cl">  <span class="n">transport</span> <span class="ss">:websocket</span><span class="p">,</span> <span class="no">Phoenix</span><span class="o">.</span><span class="n">Transports</span><span class="o">.</span><span class="n">WebSocket</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># transport :longpoll, Phoenix.Transports.LongPoll</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The &ldquo;channel&rdquo; function comes commented out, so I started by uncommenting it. You can pattern match the topic name like &ldquo;public:*&rdquo; to different Channel handlers. For this simple initial test I am sending everything to the &ldquo;RoomChannel&rdquo;, which I had to create:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">RoomChannel</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Phoenix</span><span class="o">.</span><span class="n">Channel</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Guardian</span><span class="o">.</span><span class="n">Channel</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># no auth is needed for public topics</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">join</span><span class="p">(</span><span class="s2">&#34;public:&#34;</span> <span class="o">&lt;&gt;</span> <span class="n">_topic_id</span><span class="p">,</span> <span class="n">_auth_msg</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">socket</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">join</span><span class="p">(</span><span class="n">topic</span><span class="p">,</span> <span class="sx">%{ claims: claims, resource: _resource }</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">permitted_topic?</span><span class="p">(</span><span class="n">claims</span><span class="o">[</span><span class="ss">:listen</span><span class="o">]</span><span class="p">,</span> <span class="n">topic</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="sx">%{ message: &#34;Joined&#34; }</span><span class="p">,</span> <span class="n">socket</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="ss">:error</span><span class="p">,</span> <span class="ss">:authentication_required</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">join</span><span class="p">(</span><span class="n">_room</span><span class="p">,</span> <span class="n">_payload</span><span class="p">,</span> <span class="n">_socket</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="ss">:error</span><span class="p">,</span> <span class="ss">:authentication_required</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_in</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">socket</span> <span class="o">=</span> <span class="sx">%{ topic: &#34;public:&#34; &lt;&gt; _ }</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">broadcast</span> <span class="n">socket</span><span class="p">,</span> <span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="ss">:noreply</span><span class="p">,</span> <span class="n">socket</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_in</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">claims</span> <span class="o">=</span> <span class="no">Guardian</span><span class="o">.</span><span class="n">Channel</span><span class="o">.</span><span class="n">claims</span><span class="p">(</span><span class="n">socket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">permitted_topic?</span><span class="p">(</span><span class="n">claims</span><span class="o">[</span><span class="ss">:publish</span><span class="o">]</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">topic</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">broadcast</span> <span class="n">socket</span><span class="p">,</span> <span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="ss">:noreply</span><span class="p">,</span> <span class="n">socket</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="ss">:reply</span><span class="p">,</span> <span class="ss">:error</span><span class="p">,</span> <span class="n">socket</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">permitted_topic?</span><span class="p">(</span><span class="kp">nil</span><span class="p">,</span> <span class="n">_</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">permitted_topic?</span><span class="p">(</span><span class="o">[]</span><span class="p">,</span> <span class="n">_</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">permitted_topic?</span><span class="p">(</span><span class="n">permitted_topics</span><span class="p">,</span> <span class="n">topic</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">matches</span> <span class="o">=</span> <span class="n">fn</span> <span class="n">permitted_topic</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">pattern</span> <span class="o">=</span> <span class="nb">String</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">permitted_topic</span><span class="p">,</span> <span class="s2">&#34;:*&#34;</span><span class="p">,</span> <span class="s2">&#34;:.*&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="no">Regex</span><span class="o">.</span><span class="n">match?</span><span class="p">(</span><span class="o">~</span><span class="n">r</span><span class="o">/</span><span class="p">\</span><span class="n">A</span><span class="c1">#{pattern}\z/, topic)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="no">Enum</span><span class="o">.</span><span class="n">any?</span><span class="p">(</span><span class="n">permitted_topics</span><span class="p">,</span> <span class="n">matches</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is all straight from Daniel&rsquo;s original tutorial, the important bit for this example is the first &ldquo;join&rdquo; function, the others deal with permissions and authentication that came through a JWT claim. I will deal with this in Part 2.</p>
<p>To make this work, I had to add the dependencies in &ldquo;mix.exs&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># mix.exs</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Mixfile</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Mix</span><span class="o">.</span><span class="n">Project</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">deps</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span><span class="p">{</span><span class="ss">:phoenix</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.0.3&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:phoenix_ecto</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.1&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:postgrex</span><span class="p">,</span> <span class="s2">&#34;&gt;= 0.0.0&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:phoenix_html</span><span class="p">,</span> <span class="s2">&#34;~&gt; 2.1&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:phoenix_live_reload</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.0&#34;</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="ss">:dev</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:cowboy</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.0&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:joken</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.0.0&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:guardian</span><span class="p">,</span> <span class="s2">&#34;~&gt; 0.7.0&#34;</span><span class="p">}</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And add the configuration at &ldquo;config.exs&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/config.exs</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:joken</span><span class="p">,</span> <span class="ss">config_module</span><span class="p">:</span> <span class="no">Guardian</span><span class="o">.</span><span class="n">JWT</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:guardian</span><span class="p">,</span> <span class="no">Guardian</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">issuer</span><span class="p">:</span> <span class="s2">&#34;ExPusherLite&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">ttl</span><span class="p">:</span> <span class="p">{</span> <span class="mi">30</span><span class="p">,</span> <span class="ss">:days</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="ss">verify_issuer</span><span class="p">:</span> <span class="kp">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">serializer</span><span class="p">:</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">GuardianSerializer</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">atoms</span><span class="p">:</span> <span class="o">[</span><span class="ss">:listen</span><span class="p">,</span> <span class="ss">:publish</span><span class="p">,</span> <span class="ss">:crews</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:id</span><span class="o">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now I have to add a normal HTTP POST endpoint, first adding it to the router:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/router.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Router</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:router</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">pipeline</span> <span class="ss">:browser</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:accepts</span><span class="p">,</span> <span class="o">[</span><span class="s2">&#34;html&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:fetch_session</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:fetch_flash</span>
</span></span><span class="line"><span class="cl">    <span class="c1">#plug :protect_from_forgery</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:put_secure_browser_headers</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="n">scope</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span> <span class="no">ExPusherLite</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">pipe_through</span> <span class="ss">:browser</span> <span class="c1"># Use the default browser stack</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span> <span class="no">PageController</span><span class="p">,</span> <span class="ss">:index</span>
</span></span><span class="line"><span class="cl">    <span class="n">post</span> <span class="s2">&#34;/events&#34;</span><span class="p">,</span> <span class="no">EventsController</span><span class="p">,</span> <span class="ss">:create</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice that I totally disabled CSRF token verification in the pipeline because I am not sending back Phoenix CSRF token from the Rails controller. Now, the &ldquo;EventsController&rdquo; is also almost all from Daniel&rsquo;s tutorial:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/controllers/events_controller.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">EventsController</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:controller</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="ss">:authenticate</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">topic</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="s2">&#34;topic&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">event</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="s2">&#34;event&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="s2">&#34;payload&#34;</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&#34;{}&#34;</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="no">Poison</span><span class="o">.</span><span class="n">decode!</span>
</span></span><span class="line"><span class="cl">    <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Endpoint</span><span class="o">.</span><span class="n">broadcast!</span> <span class="n">topic</span><span class="p">,</span> <span class="n">event</span><span class="p">,</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">    <span class="n">json</span> <span class="n">conn</span><span class="p">,</span> <span class="sx">%{}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">authenticate</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">secret</span> <span class="o">=</span> <span class="no">Application</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="ss">:authentication</span><span class="p">)</span><span class="o">[</span><span class="ss">:secret</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Basic &#34;</span> <span class="o">&lt;&gt;</span> <span class="n">auth_token</span> <span class="o">=</span> <span class="n">hd</span><span class="p">(</span><span class="n">get_req_header</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="s2">&#34;authorization&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="no">Plug</span><span class="o">.</span><span class="n">Crypto</span><span class="o">.</span><span class="n">secure_compare</span><span class="p">(</span><span class="n">auth_token</span><span class="p">,</span> <span class="no">Base</span><span class="o">.</span><span class="n">encode64</span><span class="p">(</span><span class="n">secret</span><span class="p">))</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span> <span class="o">|&gt;</span> <span class="n">send_resp</span><span class="p">(</span><span class="mi">401</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="n">halt</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I had to change the authenticate function a bit because either I didn&rsquo;t understand Daniel&rsquo;s implementation or it expected something different. But in this version I am just expecting a simple Basic HTTP Authentication &ldquo;authorization&rdquo; header which is a string with the format &ldquo;Basic [base64 username:password]&rdquo;. Look how I am pattern matching the string, removing the Base64 and <a href="http://codahale.com/a-lesson-in-timing-attacks/"target="_blank" rel="noopener">&ldquo;secure comparing&rdquo;</a> it (a constant-time binary compare to avoid timing attacks, this comes built-in with Phoenix).</p>
<p>This is a simple authentication technique for the Rails controller to POST the message trigger just the same as in the Pusher version.</p>
<p>And this is it, this is all it takes for this initial Phoenix-based Pusher replacement.</p>
<h2>Ex Pusher Lite - Part 2: Changing the Rails application<span class="hx:absolute hx:-mt-20" id="ex-pusher-lite---part-2-changing-the-rails-application"></span>
    <a href="#ex-pusher-lite---part-2-changing-the-rails-application" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Now that we have a bare-bones Phoenix app that we can start through &ldquo;mix phoenix.server&rdquo; and make it available at &ldquo;localhost:4000&rdquo; we can start changing the Rails application.</p>
<p>As I said in the beginning, my original wish was to use the same Pusher javascript client but change the endpoint, turns out it&rsquo;s more difficult than I thought, so I will start by removing the following line from the application layout:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;//js.pusher.com/3.0/pusher.min.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We can get rid of the Pusher gem in the Gemfile and the &ldquo;pusher.rb&rdquo; initializer as well.</p>
<p>Now, a replacement for the &ldquo;pusher.min.js&rdquo; is Phoenix&rsquo;s own &ldquo;phoenix.js&rdquo; that comes bundled in &ldquo;deps/phoenix/web/static/js/phoenix.js&rdquo;. The problem is that it is an ES6 javascript source that Phoenix passes through Brunch to be transpiled back to ES5 in every Phoenix application.</p>
<p>But I am copying this file directly to the Rails repository at &ldquo;app/assets/javascripts/phoenix.es6&rdquo;. I could change it to ES5 but I decided to go the more difficult path and just add Babel support to Rails Asset Pipeline using <a href="http://nandovieira.com/using-es6-with-asset-pipeline-on-ruby-on-rails"target="_blank" rel="noopener">Nando&rsquo;s very helpful tutorial</a> on the subject.</p>
<p>The gist goes like this, first we add the dependencies in the Gemfile:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># Use SCSS for stylesheets</span>
</span></span><span class="line"><span class="cl"><span class="c1">#gem &#39;sass-rails&#39;, &#39;~&gt; 5.0&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;sass-rails&#39;</span><span class="p">,</span> <span class="ss">github</span><span class="p">:</span> <span class="s1">&#39;rails/sass-rails&#39;</span><span class="p">,</span> <span class="ss">branch</span><span class="p">:</span> <span class="s1">&#39;master&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;sprockets-rails&#39;</span><span class="p">,</span> <span class="ss">github</span><span class="p">:</span> <span class="s1">&#39;rails/sprockets-rails&#39;</span><span class="p">,</span> <span class="ss">branch</span><span class="p">:</span> <span class="s1">&#39;master&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;sprockets&#39;</span><span class="p">,</span> <span class="ss">github</span><span class="p">:</span> <span class="s1">&#39;rails/sprockets&#39;</span><span class="p">,</span> <span class="ss">branch</span><span class="p">:</span> <span class="s1">&#39;master&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;babel-transpiler&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">source</span> <span class="s1">&#39;https://rails-assets.org&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;rails-assets-almond&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Babel needs some configuration:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/initializers/babel.rb</span>
</span></span><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">assets</span><span class="o">.</span><span class="n">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">env</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">babel</span> <span class="o">=</span> <span class="no">Sprockets</span><span class="o">::</span><span class="no">BabelProcessor</span><span class="o">.</span><span class="n">new</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;modules&#39;</span>    <span class="o">=&gt;</span> <span class="s1">&#39;amd&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;moduleIds&#39;</span>  <span class="o">=&gt;</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">env</span><span class="o">.</span><span class="n">register_transformer</span> <span class="s1">&#39;application/ecmascript-6&#39;</span><span class="p">,</span> <span class="s1">&#39;application/javascript&#39;</span><span class="p">,</span> <span class="n">babel</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And for some reason I had to manually redeclare application.js and application.css in the assets initializer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/initializers/assets.rb</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">assets</span><span class="o">.</span><span class="n">precompile</span> <span class="o">+=</span> <span class="sx">%w( application.css application.js )</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We need Almond in order to be able to import the Socket module from the Phoenix javascript package. Now, we change the &ldquo;application.js&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">//= require almond
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require jquery
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require jquery_ujs
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require turbolinks
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require phoenix
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require_tree .
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">require</span><span class="p">([</span><span class="s1">&#39;application/boot&#39;</span><span class="p">]);</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It requires an &ldquo;app/assets/javascripts/application/boot.es6&rdquo; file, this is straight from Nando&rsquo;s tutorial:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">$</span> <span class="nx">from</span> <span class="s1">&#39;jquery&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">runner</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// All scripts must live in app/assets/javascripts/application/pages/**/*.es6.
</span></span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;body&#39;</span><span class="p">).</span><span class="nx">data</span><span class="p">(</span><span class="s1">&#39;route&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Load script for this page.
</span></span></span><span class="line"><span class="cl">  <span class="c1">// We should use System.import, but it&#39;s not worth the trouble, so
</span></span></span><span class="line"><span class="cl">  <span class="c1">// let&#39;s use almond&#39;s require instead.
</span></span></span><span class="line"><span class="cl">  <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">require</span><span class="p">([</span><span class="nx">path</span><span class="p">],</span> <span class="nx">onload</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">onload</span><span class="p">(</span><span class="nx">Page</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// Instantiate the page, passing &lt;body&gt; as the root element.
</span></span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">page</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Page</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Set up page and run scripts for it.
</span></span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">page</span><span class="p">.</span><span class="nx">setup</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">page</span><span class="p">.</span><span class="nx">setup</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">page</span><span class="p">.</span><span class="nx">run</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Handles exception.
</span></span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/undefined missing/</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">warn</span><span class="p">(</span><span class="s1">&#39;missing module:&#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">).</span><span class="nx">pop</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="nb">window</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nx">ready</span><span class="p">(</span><span class="nx">runner</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;page:load&#39;</span><span class="p">,</span> <span class="nx">runner</span><span class="p">);</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And it relies on attributes in the body tag, so we change our layout template:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- app/views/layouts/application.html.erb --&gt;</span>
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span> <span class="na">data-route</span><span class="o">=</span><span class="s">&#34;application/pages/&lt;%= controller.controller_name %&gt;/&lt;%= controller.action_name %&gt;&#34;</span><span class="p">&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I didn&rsquo;t mention before but I also have a &ldquo;HomeController&rdquo; just to be the root path for the main HTML page, it has a single &ldquo;index&rdquo; method and &ldquo;index.html.erb&rdquo; template with the message form. So I will have the need for an &ldquo;application/pages/home/index.es6&rdquo; inside the &ldquo;app/assets/javascripts&rdquo; path:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span><span class="nx">Socket</span><span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;phoenix&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="kr">class</span> <span class="nx">Index</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">constructor</span><span class="p">(</span><span class="nx">root</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">root</span> <span class="o">=</span> <span class="nx">root</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// add event listeners
</span></span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;-&gt; Setting up Pusher Lite socket&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">guardianToken</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=guardian-token]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">csrfToken</span>     <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=guardian-csrf]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">pusherKey</span>     <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_key]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">pusherChannel</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_channel]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">socket</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Socket</span><span class="p">(</span><span class="s2">&#34;ws://localhost:4000/socket&#34;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">params</span><span class="o">:</span> <span class="p">{</span> <span class="nx">guardian_token</span><span class="o">:</span> <span class="nx">guardianToken</span><span class="p">,</span> <span class="nx">csrf_token</span><span class="o">:</span> <span class="nx">csrfToken</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="nx">socket</span><span class="p">.</span><span class="nx">connect</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Now that you are connected, you can join channels with a topic:
</span></span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">channel</span> <span class="o">=</span> <span class="nx">socket</span><span class="p">.</span><span class="nx">channel</span><span class="p">(</span><span class="nx">pusherChannel</span><span class="p">,</span> <span class="p">{})</span>
</span></span><span class="line"><span class="cl">    <span class="nx">channel</span><span class="p">.</span><span class="nx">join</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">      <span class="p">.</span><span class="nx">receive</span><span class="p">(</span><span class="s2">&#34;ok&#34;</span><span class="p">,</span> <span class="nx">resp</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Joined successfully&#34;</span><span class="p">,</span> <span class="nx">resp</span><span class="p">)</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">      <span class="p">.</span><span class="nx">receive</span><span class="p">(</span><span class="s2">&#34;error&#34;</span><span class="p">,</span> <span class="nx">resp</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Unable to join&#34;</span><span class="p">,</span> <span class="nx">resp</span><span class="p">)</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">channel</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="nx">data</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kd">let</span> <span class="nx">new_line</span> <span class="o">=</span> <span class="sb">`&lt;p&gt;&lt;strong&gt;</span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">&lt;strong&gt;: </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">message</span><span class="si">}</span><span class="sb">&lt;/p&gt;`</span>
</span></span><span class="line"><span class="cl">      <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;.message-receiver&#34;</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">new_line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">run</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// trigger initial action (e.g. perform http requests)
</span></span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;-&gt; perform initial actions&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This bit is similar to the Pusher javascript handling, but we are getting a bit more information from the meta tags, the &ldquo;guardian-token&rdquo; and &ldquo;guardian-csrf&rdquo; tokens. Because I was following Daniel&rsquo;s tutorial I also changed the name of the event from &ldquo;new_message&rdquo; to just &ldquo;msg&rdquo; and the topics now need to have a &ldquo;public:&rdquo; prefix in order for the Phoenix&rsquo;s RoomChannel handler to match the public topic name correctly.</p>
<p>First things first. In order for this new javascript to have the correct tokens I had to add the following helper in the views layout:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">  <span class="err">&lt;</span>%= csrf_meta_tags %&gt;
</span></span><span class="line"><span class="cl">  <span class="err">&lt;</span>%= guardian_token_tags %&gt;
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">...</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this &ldquo;guardian_token_tags&rdquo; is again straight from Daniel&rsquo;s tutorial:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">GuardianHelper</span>
</span></span><span class="line"><span class="cl">  <span class="no">ISSUER</span> <span class="o">=</span> <span class="s2">&#34;pl-web-</span><span class="si">#{</span><span class="no">Rails</span><span class="o">.</span><span class="n">env</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="no">DIGEST</span> <span class="o">=</span> <span class="no">OpenSSL</span><span class="o">::</span><span class="no">Digest</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;sha256&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">guardian_token_tags</span>
</span></span><span class="line"><span class="cl">    <span class="n">token</span> <span class="o">=</span> <span class="no">Base64</span><span class="o">.</span><span class="n">urlsafe_encode64</span><span class="p">(</span><span class="no">SecureRandom</span><span class="o">.</span><span class="n">random_bytes</span><span class="p">(</span><span class="mi">32</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;&lt;meta content=</span><span class="se">\&#34;</span><span class="si">#{</span><span class="n">jwt</span><span class="p">(</span><span class="n">token</span><span class="p">)</span><span class="si">}</span><span class="se">\&#34;</span><span class="s2"> name=</span><span class="se">\&#34;</span><span class="s2">guardian-csrf</span><span class="se">\&#34;</span><span class="s2"> /&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;&lt;meta content=</span><span class="se">\&#34;</span><span class="si">#{</span><span class="n">token</span><span class="si">}</span><span class="se">\&#34;</span><span class="s2"> name=</span><span class="se">\&#34;</span><span class="s2">guardian-token</span><span class="se">\&#34;</span><span class="s2"> /&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="o">].</span><span class="n">shuffle</span><span class="o">.</span><span class="n">join</span><span class="o">.</span><span class="n">html_safe</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">jwt</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">JWT</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">jwt_claims</span><span class="p">(</span><span class="n">token</span><span class="p">),</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_key</span><span class="p">,</span> <span class="s1">&#39;HS256&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">jwt_claims</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="ss">aud</span><span class="p">:</span> <span class="ss">:csrf</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nb">sub</span><span class="p">:</span> <span class="n">jwt_sub</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">iss</span><span class="p">:</span> <span class="no">ISSUER</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">iat</span><span class="p">:</span> <span class="no">Time</span><span class="o">.</span><span class="n">now</span><span class="o">.</span><span class="n">utc</span><span class="o">.</span><span class="n">to_i</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">exp</span><span class="p">:</span> <span class="p">(</span><span class="no">Time</span><span class="o">.</span><span class="n">now</span> <span class="o">+</span> <span class="mi">30</span><span class="o">.</span><span class="n">days</span><span class="p">)</span><span class="o">.</span><span class="n">utc</span><span class="o">.</span><span class="n">to_i</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">s_csrf</span><span class="p">:</span> <span class="n">guardian_signed_token</span><span class="p">(</span><span class="n">token</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="ss">listen</span><span class="p">:</span> <span class="n">jwt_listens</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">publish</span><span class="p">:</span> <span class="n">jwt_publish</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">jwt_sub</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{}</span> <span class="k">unless</span> <span class="n">current_human</span><span class="o">.</span><span class="n">present?</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nb">id</span><span class="p">:</span> <span class="n">current_human</span><span class="o">.</span><span class="n">id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nb">name</span><span class="p">:</span> <span class="n">current_human</span><span class="o">.</span><span class="n">full_name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">email</span><span class="p">:</span> <span class="n">current_human</span><span class="o">.</span><span class="n">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">crews</span><span class="p">:</span> <span class="n">current_human</span><span class="o">.</span><span class="n">crews</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:identifier</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">jwt_listens</span>
</span></span><span class="line"><span class="cl">    <span class="n">listens</span> <span class="o">=</span> <span class="o">[</span><span class="s1">&#39;deploys:web&#39;</span><span class="p">,</span> <span class="s1">&#39;public:*&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">listens</span><span class="o">.</span><span class="n">push</span><span class="p">(</span><span class="s1">&#39;private:*&#39;</span><span class="p">)</span> <span class="k">if</span> <span class="n">current_human</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="ss">:in_crew?</span><span class="p">,</span> <span class="ss">:admins</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">listens</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">jwt_publish</span>
</span></span><span class="line"><span class="cl">    <span class="n">publish</span> <span class="o">=</span> <span class="o">[</span><span class="s1">&#39;public:*&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">publish</span><span class="o">.</span><span class="n">push</span><span class="p">(</span><span class="s1">&#39;private:*&#39;</span><span class="p">)</span> <span class="k">if</span> <span class="n">current_human</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="ss">:in_crew?</span><span class="p">,</span> <span class="ss">:admins</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">publish</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">guardian_signed_token</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">key</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_key</span>
</span></span><span class="line"><span class="cl">    <span class="n">signed_token</span> <span class="o">=</span> <span class="no">OpenSSL</span><span class="o">::</span><span class="no">HMAC</span><span class="o">.</span><span class="n">digest</span><span class="p">(</span><span class="no">DIGEST</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Base64</span><span class="o">.</span><span class="n">urlsafe_encode64</span><span class="p">(</span><span class="n">signed_token</span><span class="p">)</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/={1,}$/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I had to tweak it a bit, specially to get the proper keys from the &ldquo;secrets.yml&rdquo; file which now looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">development</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">secret_key_base</span><span class="p">:</span><span class="w"> </span><span class="l">ded7c4a2a298c1b620e462b50c9ca6ccb60130e27968357e76cab73de9858f14556a26df885c8aa5004d0a7ca79c0438e618557275bdb28ba67a0ffb0c268056</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_url</span><span class="p">:</span><span class="w"> </span><span class="l">http://&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;:&lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;@&lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_key</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_secret</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_channel</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;public:test_chat_channel&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">secret_key_base</span><span class="p">:</span><span class="w"> </span><span class="l">f51ff494801ff0f9e1711036ef6f2f6f1e13544b02326adc5629c6833ae90f1a476747fae94b792eba8a444305df8e7a5ad53f05ea4234692ac96cc44f372029</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_url</span><span class="p">:</span><span class="w"> </span><span class="l">http://&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;:&lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;@&lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_key</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_secret</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_channel</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;public:test_chat_channel&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># Do not keep production secrets in the repository,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># instead read values from the environment.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">production</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">secret_key_base</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#34;SECRET_KEY_BASE&#34;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_url</span><span class="p">:</span><span class="w"> </span><span class="l">http://&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;:&lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;@&lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_key</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_secret</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_channel</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_CHANNEL&#39;] %&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>My local &ldquo;.env&rdquo; file looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>PUSHER_URL: &#34;localhost:4000&#34;
PUSHER_KEY: &#34;14e86e5fee3335fa88b0&#34;
PUSHER_SECRET: &#34;2b94ff0f07ce9769567f&#34;
PUSHER_CHANNEL: &#34;public:test_chat_channel&#34; </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This bit needs more work, I know. I just copied Pusher&rsquo;s key and Pusher&rsquo;s password as KEY and SECRET. This is the bit I mentioned I tweaked in the RoomChannel&rsquo;s authenticate function in the Phoenix side.</p>
<p>Now that I have this in place, I have to change the &ldquo;PusherEvent&rdquo; model to trigger the message from the form to the Phoenix&rsquo;s EventsController, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/models/event.rb</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;net/http&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;uri&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PusherEvent</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">ActiveModel</span><span class="o">::</span><span class="no">Model</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">save</span>
</span></span><span class="line"><span class="cl">    <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_url</span><span class="si">}</span><span class="s2">/events&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">post_form</span><span class="p">(</span><span class="n">uri</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;topic&#34;</span> <span class="o">=&gt;</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_channel</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;event&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;msg&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;payload&#34;</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span> <span class="o">=&gt;</span> <span class="nb">name</span><span class="p">,</span> <span class="s2">&#34;message&#34;</span> <span class="o">=&gt;</span> <span class="n">message</span><span class="p">}</span><span class="o">.</span><span class="n">to_json</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As I am doing this through SuckerPunch, I am using plain old &ldquo;Net::HTTP.post()&rdquo; to post the message to the Phoenix &ldquo;/events&rdquo; endpoint. Phoenix will properly authenticate because the &ldquo;pusher_url&rdquo; is sending the &ldquo;PUSHER_KEY:PUSHER_SECRET&rdquo; as HTTP Basic Auth. It will end up in the &ldquo;authorization&rdquo; header and Phoenix will properly authenticate the server side, then it will broadcast to the WebSocket connections subscribed to the topic.</p>
<p>The new javascript will subscribe to the &ldquo;public:test_chat_channel&rdquo; topic and listen to the &ldquo;msg&rdquo; event. Once it receives the payload, it just formats the message and, again, appends to the same place in the &ldquo;message-receiver&rdquo; div tag.</p>
<h3>Conclusion: Further Work<span class="hx:absolute hx:-mt-20" id="conclusion-further-work"></span>
    <a href="#conclusion-further-work" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>So, with this we have exactly the same behavior as the Pusher version, but now it&rsquo;s under my control.</p>
<p>The idea is for the Phoenix app to have apps, a real authentication for different apps. Then every Rails app I do can just connect to this same Phoenix service.</p>
<p>The next steps include properly implementing the Guardian/JWT pieces, then I can jump to private channels support and add HTTP APIs to list channels in apps and users online in channels.</p>
<p>I will then create a second companion Rails app as an administration dashboard to consume those APIs and be able to create or revoke apps and basic maintenance and reporting. This should be a good enough replacement for a Pusher-like messaging solution that is really fast.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>rails</category><category>distributed-systems</category></item><item><title>Erlang's Ping Pong (tut15) in Clojure and Elixir</title><link>https://akitaonrails.github.io/en/2015/12/09/erlang-s-ping-pong-tut15-in-clojure-and-elixir/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/09/erlang-s-ping-pong-tut15-in-clojure-and-elixir/</guid><pubDate>Wed, 09 Dec 2015 14:42:00 -0200</pubDate><description>&lt;p&gt;This is a very short post just because I thought it was fun. I was reading &lt;a href="http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/"target="_blank" rel="noopener"&gt;this very enlightening article&lt;/a&gt; on Clojure&amp;rsquo;s Quasar/Pulsar compared to Erlang and how they are trying to plug the holes on the JVM shortcomings.&lt;/p&gt;
&lt;p&gt;When you&amp;rsquo;re learning Erlang through its official documentation, the first thing you build in the &lt;a href="http://www.erlang.org/doc/getting_started/conc_prog.html#id67347"target="_blank" rel="noopener"&gt;chapters on Processes&lt;/a&gt; is a very simple Ping Pong code that looks like this:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tut15&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;export&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ping&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pong&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Pong_PID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Pong_PID&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;finished&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;ping finished&lt;/span&gt;&lt;span class="si"&gt;~n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Pong_PID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Pong_PID&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ping&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;receive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pong&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Ping received pong&lt;/span&gt;&lt;span class="si"&gt;~n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;N&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="nv"&gt;Pong_PID&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;pong&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;receive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;finished&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Pong finished&lt;/span&gt;&lt;span class="si"&gt;~n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Ping_PID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Pong received ping&lt;/span&gt;&lt;span class="si"&gt;~n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Ping_PID&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;pong&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pong&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Pong_PID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tut15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pong&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tut15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;,&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="nv"&gt;Pong_PID&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;It&amp;rsquo;s not pretty, it&amp;rsquo;s Prolog-ish. The Clojure article claims how close they got with lightweight threads (true green threads) and this is the same exercise done in Clojure:&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is a very short post just because I thought it was fun. I was reading <a href="http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/"target="_blank" rel="noopener">this very enlightening article</a> on Clojure&rsquo;s Quasar/Pulsar compared to Erlang and how they are trying to plug the holes on the JVM shortcomings.</p>
<p>When you&rsquo;re learning Erlang through its official documentation, the first thing you build in the <a href="http://www.erlang.org/doc/getting_started/conc_prog.html#id67347"target="_blank" rel="noopener">chapters on Processes</a> is a very simple Ping Pong code that looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">tut15</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">start</span><span class="o">/</span><span class="mi">0</span><span class="p">,</span> <span class="n">ping</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="n">pong</span><span class="o">/</span><span class="mi">0</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">ping</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nv">Pong_PID</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Pong_PID</span> <span class="o">!</span> <span class="n">finished</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;ping finished</span><span class="si">~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">ping</span><span class="p">(</span><span class="nv">N</span><span class="p">,</span> <span class="nv">Pong_PID</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Pong_PID</span> <span class="o">!</span> <span class="p">{</span><span class="n">ping</span><span class="p">,</span> <span class="n">self</span><span class="p">()},</span>
</span></span><span class="line"><span class="cl">    <span class="k">receive</span>
</span></span><span class="line"><span class="cl">        <span class="n">pong</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;Ping received pong</span><span class="si">~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[])</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">ping</span><span class="p">(</span><span class="nv">N</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">Pong_PID</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">pong</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">receive</span>
</span></span><span class="line"><span class="cl">        <span class="n">finished</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;Pong finished</span><span class="si">~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="n">ping</span><span class="p">,</span> <span class="nv">Ping_PID</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;Pong received ping</span><span class="si">~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[]),</span>
</span></span><span class="line"><span class="cl">            <span class="nv">Ping_PID</span> <span class="o">!</span> <span class="n">pong</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">pong</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">start</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Pong_PID</span> <span class="o">=</span> <span class="nb">spawn</span><span class="p">(</span><span class="n">tut15</span><span class="p">,</span> <span class="n">pong</span><span class="p">,</span> <span class="p">[]),</span>
</span></span><span class="line"><span class="cl">    <span class="nb">spawn</span><span class="p">(</span><span class="n">tut15</span><span class="p">,</span> <span class="n">ping</span><span class="p">,</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="nv">Pong_PID</span><span class="p">]).</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s not pretty, it&rsquo;s Prolog-ish. The Clojure article claims how close they got with lightweight threads (true green threads) and this is the same exercise done in Clojure:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lisp" data-lang="lisp"><span class="line"><span class="cl"><span class="p">(</span><span class="nv">defsfn</span> <span class="nv">pong</span> <span class="nv">[]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nv">==</span> <span class="nv">n</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nb">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">!</span> <span class="ss">:pong</span> <span class="ss">:finished</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">println</span> <span class="s">&#34;ping finished&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nb">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">!</span> <span class="ss">:pong</span> <span class="nv">[:ping</span> <span class="nv">@self]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">receive</span>
</span></span><span class="line"><span class="cl">       <span class="ss">:pong</span> <span class="p">(</span><span class="nv">println</span> <span class="s">&#34;Ping received pong&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">recur</span> <span class="p">(</span><span class="nv">dec</span> <span class="nv">n</span><span class="p">)))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nv">defsfn</span> <span class="nv">pong</span> <span class="nv">[]</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nv">receive</span>
</span></span><span class="line"><span class="cl">   <span class="ss">:finished</span> <span class="p">(</span><span class="nv">println</span> <span class="s">&#34;Pong finished&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="nv">[:ping</span> <span class="nv">ping]</span> <span class="p">(</span><span class="nb">do</span>
</span></span><span class="line"><span class="cl">                  <span class="p">(</span><span class="nv">println</span> <span class="s">&#34;Pong received ping&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                  <span class="p">(</span><span class="nv">!</span> <span class="nv">ping</span> <span class="ss">:pong</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                  <span class="p">(</span><span class="nv">recur</span><span class="p">))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nv">defn</span> <span class="nv">-main</span> <span class="nv">[]</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nv">register</span> <span class="ss">:pong</span> <span class="p">(</span><span class="nv">spawn</span> <span class="nv">pong</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nv">spawn</span> <span class="nv">ping</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:ok</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>People that like the Lisp-y aesthetics of programming directly in the AST representation through the structure of parenthesis based blocks will find this very pretty.</p>
<p>I personally spent years looking into code like this (Common Lisp, Elisp, Scheme, etc) and I still can&rsquo;t get used to it. Once you have a competent editor such as Emacs, that can deal with the proper parenthesis handling, it&rsquo;s easier, yes, but I still can&rsquo;t find the joy in doing this kind of hackish syntax.</p>
<p>Elixir is much more than a new syntax on top of Erlang, as the great book <a href="https://pragprog.com/book/cmelixir/metaprogramming-elixir"target="_blank" rel="noopener">Metaprogramming Elixir</a> will teach you. It opens up the entire Erlang BEAM AST through the usage of the <a href="http://elixir-lang.org/getting-started/meta/quote-and-unquote.html"target="_blank" rel="noopener">quote/unquote</a> mechanics, making programming directly into the AST through <a href="http://elixir-lang.org/getting-started/meta/macros.html"target="_blank" rel="noopener">&ldquo;Hygienic Macros&rdquo;</a> a breeze. It&rsquo;s really the best of both worlds of having a modern, good looking, joyful syntax and the same power a Lisp-y language gives you in terms of well behaved macros.</p>
<p>Now, this is the same example as above, in Elixir:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPingPongTut15</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">ping</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">pong_pid</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nb">send</span> <span class="n">pong_pid</span><span class="p">,</span> <span class="ss">:finished</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="s2">&#34;ping finished&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">ping</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">pong_pid</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nb">send</span> <span class="n">pong_pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:ping</span><span class="p">,</span> <span class="nb">self</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">receive</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="ss">:pong</span> <span class="o">-&gt;</span> <span class="no">IO</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="s2">&#34;Ping received pong&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">ping</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">pong_pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">pong</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">receive</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="ss">:finished</span> <span class="o">-&gt;</span> <span class="no">IO</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="s2">&#34;Pong finished&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:ping</span><span class="p">,</span> <span class="n">ping_pid</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="no">IO</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="s2">&#34;Pong received ping&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">send</span> <span class="n">ping_pid</span><span class="p">,</span> <span class="ss">:pong</span>
</span></span><span class="line"><span class="cl">        <span class="n">pong</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">pong_pid</span> <span class="o">=</span> <span class="n">spawn</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span> <span class="n">pong</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">spawn</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span> <span class="n">ping</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">pong_pid</span><span class="p">)</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Because of the power of pattern matching in the function argument signatures, you can define 2 separated functions, avoiding the &ldquo;if&rdquo; as in the Clojure example. Of course, because it&rsquo;s Clojure and it has a complete macro system, and because its <a href="https://github.com/clojure/core.match"target="_blank" rel="noopener">core.match</a> has the proper pattern matching mechanisms, you can emulate the same thing through external libraries such as <a href="https://github.com/killme2008/defun"target="_blank" rel="noopener">defun</a> though.</p>
<p>This was just a simple exercise, I hope it shed some light on the basic similarities between these 3 languages. And as &ldquo;ugly&rdquo; as Erlang may feel, I still feel more comfortable with its quirks than nested parenthesis.</p>
]]></content:encoded><category>programming-languages</category><category>elixir-and-erlang</category><category>learning</category></item><item><title>Ex Manga Downloadr - Part 5: Making it more robust!</title><link>https://akitaonrails.github.io/en/2015/12/06/ex-manga-downloadr-part-5-making-it-more-robust/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/06/ex-manga-downloadr-part-5-making-it-more-robust/</guid><pubDate>Sun, 06 Dec 2015 19:20:00 -0200</pubDate><description>&lt;p&gt;And there I go again. I know some of you may be bored by this tool already, but as a playground project, I still want to make this good code. But there are 2 big problems right now.&lt;/p&gt;
&lt;p&gt;When I was testing only with MangaReader.net as a source, everything worked almost flawlessly. But adding MangaFox in &lt;a href="http://www.akitaonrails.com/en/2015/12/02/ex-manga-downloadr-parte-3-suporte-ao-mangafox"target="_blank" rel="noopener"&gt;Part 3&lt;/a&gt;, with its more restrictive rules towards scraper tools like mine (timing out more frequently, not allowing too many connections from the same place, etc), the process just kept crashing and I had to manually restart it (the resuming features I added in &lt;a href="http://www.akitaonrails.com/en/2015/12/03/ex-manga-downloadr-parte-4-aprendendo-atraves-do-refactoring"target="_blank" rel="noopener"&gt;Part 4&lt;/a&gt; paid off, but it&amp;rsquo;s not a reliable tool anymore).&lt;/p&gt;</description><content:encoded><![CDATA[<p>And there I go again. I know some of you may be bored by this tool already, but as a playground project, I still want to make this good code. But there are 2 big problems right now.</p>
<p>When I was testing only with MangaReader.net as a source, everything worked almost flawlessly. But adding MangaFox in <a href="http://www.akitaonrails.com/en/2015/12/02/ex-manga-downloadr-parte-3-suporte-ao-mangafox"target="_blank" rel="noopener">Part 3</a>, with its more restrictive rules towards scraper tools like mine (timing out more frequently, not allowing too many connections from the same place, etc), the process just kept crashing and I had to manually restart it (the resuming features I added in <a href="http://www.akitaonrails.com/en/2015/12/03/ex-manga-downloadr-parte-4-aprendendo-atraves-do-refactoring"target="_blank" rel="noopener">Part 4</a> paid off, but it&rsquo;s not a reliable tool anymore).</p>
<p>To recap, the Workflow just organizes each step of the process. Its functions are similar to this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">process_downloads</span><span class="p">(</span><span class="n">images_list</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">images_list</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Worker</span><span class="o">.</span><span class="n">page_download_image</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="n">directory</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Task</span><span class="o">.</span><span class="n">await</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="vi">@await_timeout_ms</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="n">directory</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It deals with a large list, maps over each element sending it to a Worker function to run, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">page_download_image</span><span class="p">(</span><span class="n">image_data</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Task</span><span class="o">.</span><span class="n">async</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:poolboy</span><span class="o">.</span><span class="n">transaction</span> <span class="ss">:worker_pool</span><span class="p">,</span> <span class="n">fn</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="p">{</span><span class="ss">:page_download_image</span><span class="p">,</span> <span class="n">image_data</span><span class="p">,</span> <span class="n">directory</span><span class="p">},</span> <span class="vi">@genserver_call_timeout</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">,</span> <span class="vi">@task_async_timeout</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It returns an asynchronous Task waiting for 2 things: for Poolboy to release a free process to use, and for the Worker/GenServer function to finish running inside that process. As I explained in <a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener">Part 2</a> this is so we can limit the maximum number of connections to the external source. If we didn&rsquo;t have this restriction, sending tens of thousands of asynchronous requests at once, the external source would just fail them all.</p>
<p>The first thing to bear in mind is that a &ldquo;<tt>Task.async/2</tt>&rdquo; links itself to the caller process, so if something goes wrong, the parent process dies as well.</p>
<p>The correct thing to do is to add a <a href="http://elixir-lang.org/docs/stable/elixir/Task.Supervisor.html"target="_blank" rel="noopener">Task.Supervisor</a> and make it deal with each Task child. To do that, we can just add the Supervisor in our supervised tree at &ldquo;pool_management/supervisor.ex&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">PoolManagement</span><span class="o">.</span><span class="n">Supervisor</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Supervisor</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="n">children</span> <span class="o">=</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">    <span class="n">supervisor</span><span class="p">(</span><span class="no">Task</span><span class="o">.</span><span class="n">Supervisor</span><span class="p">,</span> <span class="o">[[</span><span class="nb">name</span><span class="p">:</span> <span class="no">Fetcher</span><span class="o">.</span><span class="n">TaskSupervisor</span><span class="o">]]</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:poolboy</span><span class="o">.</span><span class="n">child_spec</span><span class="p">(</span><span class="ss">:worker_pool</span><span class="p">,</span> <span class="n">pool_options</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And we can replace the &ldquo;<tt>Task.async/2</tt>&rdquo; calls to &ldquo;<tt>Task.Supervisor.async(Fetcher.TaskSupervisor, &hellip;)</tt>&rdquo; like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">page_download_image</span><span class="p">(</span><span class="n">image_data</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Task</span><span class="o">.</span><span class="n">Supervisor</span><span class="o">.</span><span class="n">async</span><span class="p">(</span><span class="no">Fetcher</span><span class="o">.</span><span class="n">TaskSupervisor</span><span class="p">,</span> <span class="n">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:poolboy</span><span class="o">.</span><span class="n">transaction</span> <span class="ss">:worker_pool</span><span class="p">,</span> <span class="n">fn</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="p">{</span><span class="ss">:page_download_image</span><span class="p">,</span> <span class="n">image_data</span><span class="p">,</span> <span class="n">directory</span><span class="p">},</span> <span class="vi">@genserver_call_timeout</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">,</span> <span class="vi">@task_async_timeout</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This still creates Tasks that we need to await on, and as before, if the function inside crashes, it still brings down the main process. Now my refactoring found a dead end.</p>
<p>This is the 2nd problem I mentioned in the beginning of the article: a <strong>flaw in my design</strong>.</p>
<p>Instead of just mapping through each element of a large list, I should have created an Agent based GenServer to keep the list as the state and make the entire Workflow system a new supervised GenServer. If fetching one URL crashed the GenServer, its supervisor would restart it and pick up the next element in the list.</p>
<p>But, as I am in no mood for this refactoring right now (it&rsquo;s Sunday afternoon) I will concentrate on a quick fix (yes, jerry-rigged patch), just so the function in the async call does not raise exceptions.</p>
<h3>OMG! It&rsquo;s a Try/Catch block!<span class="hx:absolute hx:-mt-20" id="omg-its-a-trycatch-block"></span>
    <a href="#omg-its-a-trycatch-block" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Turns out that everything I run inside the Poolboy processes are HTTP get requests through HTTPotion. Fortunately I had already refactored every HTTPotion get call into a neat macro:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmacro</span> <span class="n">fetch</span><span class="p">(</span><span class="n">link</span><span class="p">,</span> <span class="k">do</span><span class="p">:</span> <span class="n">expression</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">quote</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&#34;Fetching from </span><span class="si">#{</span><span class="n">unquote</span><span class="p">(</span><span class="n">link</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="no">HTTPotion</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">unquote</span><span class="p">(</span><span class="n">link</span><span class="p">),</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">http_headers</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="o">%</span><span class="no">HTTPotion</span><span class="o">.</span><span class="n">Response</span><span class="p">{</span> <span class="ss">body</span><span class="p">:</span> <span class="n">body</span><span class="p">,</span> <span class="ss">headers</span><span class="p">:</span> <span class="n">headers</span><span class="p">,</span> <span class="ss">status_code</span><span class="p">:</span> <span class="mi">200</span> <span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="n">body</span> <span class="o">|&gt;</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">gunzip</span><span class="p">(</span><span class="n">headers</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="n">unquote</span><span class="p">(</span><span class="n">expression</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="n">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span> <span class="ss">:err</span><span class="p">,</span> <span class="s2">&#34;not found&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now I only need to replace 1 line in this macro:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">-</span>    <span class="k">case</span> <span class="no">HTTPotion</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">unquote</span><span class="p">(</span><span class="n">link</span><span class="p">),</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">http_headers</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="k">case</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">retryable_http_get</span><span class="p">(</span><span class="n">unquote</span><span class="p">(</span><span class="n">link</span><span class="p">))</span> <span class="k">do</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And define this new retryable logic in the main module:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMangaDownloadr</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="nb">require</span> <span class="no">Logger</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># will retry failed fetches over 50 times, sleeping 1 second between each retry</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@max_retries</span>  <span class="mi">50</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@time_to_wait_to_fetch_again</span> <span class="mi">1_000</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">retryable_http_get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="k">raise</span> <span class="s2">&#34;Failed to fetch from </span><span class="si">#{</span><span class="n">url</span><span class="si">}</span><span class="s2"> after </span><span class="si">#{</span><span class="vi">@max_retries</span><span class="si">}</span><span class="s2"> retries.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">retryable_http_get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">retries</span> <span class="p">\\</span> <span class="vi">@max_retries</span><span class="p">)</span> <span class="k">when</span> <span class="n">retries</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">try</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&#34;Fetching from </span><span class="si">#{</span><span class="n">url</span><span class="si">}</span><span class="s2"> for the </span><span class="si">#{</span><span class="vi">@max_retries</span> <span class="o">-</span> <span class="n">retries</span><span class="si">}</span><span class="s2"> time.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">response</span> <span class="o">=</span> <span class="no">HTTPotion</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">http_headers</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">case</span> <span class="n">response</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="o">%</span><span class="no">HTTPotion</span><span class="o">.</span><span class="n">Response</span><span class="p">{</span> <span class="ss">body</span><span class="p">:</span> <span class="n">_</span><span class="p">,</span> <span class="ss">headers</span><span class="p">:</span> <span class="n">_</span><span class="p">,</span> <span class="ss">status_code</span><span class="p">:</span> <span class="n">status</span> <span class="p">}</span> <span class="k">when</span> <span class="n">status</span> <span class="o">&gt;</span> <span class="mi">499</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="k">raise</span> <span class="o">%</span><span class="no">HTTPotion</span><span class="o">.</span><span class="n">HTTPError</span><span class="p">{</span><span class="ss">message</span><span class="p">:</span> <span class="s2">&#34;req_timedout&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="n">response</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">rescue</span>
</span></span><span class="line"><span class="cl">      <span class="n">error</span> <span class="k">in</span> <span class="no">HTTPotion</span><span class="o">.</span><span class="n">HTTPError</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="n">error</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">          <span class="o">%</span><span class="no">HTTPotion</span><span class="o">.</span><span class="n">HTTPError</span><span class="p">{</span><span class="ss">message</span><span class="p">:</span> <span class="n">message</span><span class="p">}</span> <span class="k">when</span> <span class="n">message</span> <span class="k">in</span> <span class="vi">@http_errors</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="ss">:timer</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="vi">@time_to_wait_to_fetch_again</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">retryable_http_get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">retries</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="n">_</span> <span class="o">-&gt;</span> <span class="k">raise</span> <span class="n">error</span>
</span></span><span class="line"><span class="cl">        <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I strongly <a href="http://www.akitaonrails.com/en/2015/12/01/o-obrigatorio-por-que-elixir-visao-pessoal"target="_blank" rel="noopener">stated</a> that in Elixir we should <strong>not</strong> use &ldquo;try/catch&rdquo; blocks, but there you have it.</p>
<p>This is the consequence of the flaw in my initial Workflow design. If I had coded the Workflow module to be a GenServer, with each list managed by an Agent, each failed HTTPotion call would allow the supervisor to restart it and try again. Without resorting to the ugly &ldquo;try/catch&rdquo; code.</p>
<p>Maybe this will force me to write Part 6 as being the code to remove this ugly try/catch later, so consider this a <strong>Technical Debt</strong> to make everything work now so we can refactor later and pay the debt back.</p>
<p>&ldquo;<tt>HTTPotion.get/2</tt>&rdquo; calls can raise &ldquo;HTTPotion.HTTPError&rdquo; exceptions. I am catching those errors for the time being, matching the messages against a list of errors I had already, sleeping for a certain amount of time (just a heuristic to see if the external sources respond better that way) and I recurse to itself through a limited number of &ldquo;retries&rdquo;, until it reaches zero, in which case it may even be the case that the internet connection is down or some other severe error that we would not be able to recover soon.</p>
<p>With this code in place, now even fetching from MangaFox, without tweaking down the POOL_SIZE, will run until the end, and this solves my needs for now. If anyone is interested in suggesting a better, GenServer based Workflow design, I would really appreciate a Pull Request.</p>
<p>Cheers.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>software-engineering</category><category>testing</category></item><item><title>Ex Manga Downloadr - Part 4: Learning through Refactoring</title><link>https://akitaonrails.github.io/en/2015/12/03/ex-manga-downloadr-part-4-learning-through-refactoring/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/03/ex-manga-downloadr-part-4-learning-through-refactoring/</guid><pubDate>Thu, 03 Dec 2015 17:40:00 -0200</pubDate><description>&lt;p&gt;&lt;a href="http://www.akitaonrails.com/en/2015/12/02/ex-manga-downloadr-parte-3-suporte-ao-mangafox"target="_blank" rel="noopener"&gt;Yesterday I added Mangafox support&lt;/a&gt; to my downloader tool and it also added a bit of dirty code into my already not-so-good coding. It&amp;rsquo;s time for some serious cleanup.&lt;/p&gt;
&lt;p&gt;You can see everything I did since yesterday to clean things up through &lt;a href="https://github.com/akitaonrails/ex_manga_downloadr/compare/59694565592f8a3bea95115b858dd2ddfdc89873...3ae7dd98a8fd7bae47ffd7a24c0c42a2c3777fad"target="_blank" rel="noopener"&gt;Github&amp;rsquo;s awesome compare page&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;First things first: now the choice to have added a reasonable amount of tests will pay off. In this refactoring I changed function signatures, response formats, moved a fair amount of code around, and without the tests this endeavor would have taken me the entire day or more, rendering the refactor efforts questionable to begin with.&lt;/p&gt;</description><content:encoded><![CDATA[<p><a href="http://www.akitaonrails.com/en/2015/12/02/ex-manga-downloadr-parte-3-suporte-ao-mangafox"target="_blank" rel="noopener">Yesterday I added Mangafox support</a> to my downloader tool and it also added a bit of dirty code into my already not-so-good coding. It&rsquo;s time for some serious cleanup.</p>
<p>You can see everything I did since yesterday to clean things up through <a href="https://github.com/akitaonrails/ex_manga_downloadr/compare/59694565592f8a3bea95115b858dd2ddfdc89873...3ae7dd98a8fd7bae47ffd7a24c0c42a2c3777fad"target="_blank" rel="noopener">Github&rsquo;s awesome compare page</a></p>
<p>First things first: now the choice to have added a reasonable amount of tests will pay off. In this refactoring I changed function signatures, response formats, moved a fair amount of code around, and without the tests this endeavor would have taken me the entire day or more, rendering the refactor efforts questionable to begin with.</p>
<p>At each step of the refactoring I could run &ldquo;<tt>mix test</tt>&rdquo; and work until I ended up with the green status:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Finished in 13.5 seconds (0.1s on load, 13.4s on tests)
12 tests, 0 failures</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The tests take a long time because I made a choice for the MangaReader and Mangafox unit tests to actually go online and fetch from the sites. It takes longer to run the suite but I know that if it breaks and I didn&rsquo;t touch that code, the source websites changed their formats and I need to change the parser. I could have added fixtures to make the tests run faster, but the point of my parser is for them to be correct.</p>
<h3>Macros to the Rescue!<span class="hx:absolute hx:-mt-20" id="macros-to-the-rescue"></span>
    <a href="#macros-to-the-rescue" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Each source module has 3 sub-modules: ChapterPage, IndexPage and Page. All of them have a main function that resembles this piece of code:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">Mangafox</span><span class="o">.</span><span class="n">ChapterPage</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="nb">require</span> <span class="no">Logger</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">pages</span><span class="p">(</span><span class="n">chapter_link</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&#34;Fetching pages from chapter </span><span class="si">#{</span><span class="n">chapter_link</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="no">HTTPotion</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">chapter_link</span><span class="p">,</span> <span class="o">[</span><span class="ss">headers</span><span class="p">:</span> <span class="o">[</span><span class="s2">&#34;User-Agent&#34;</span><span class="p">:</span> <span class="vi">@user_agent</span><span class="p">,</span> <span class="s2">&#34;Accept-encoding&#34;</span><span class="p">:</span> <span class="s2">&#34;gzip&#34;</span><span class="o">]</span><span class="p">,</span> <span class="ss">timeout</span><span class="p">:</span> <span class="mi">30_000</span><span class="o">]</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="o">%</span><span class="no">HTTPotion</span><span class="o">.</span><span class="n">Response</span><span class="p">{</span> <span class="ss">body</span><span class="p">:</span> <span class="n">body</span><span class="p">,</span> <span class="ss">headers</span><span class="p">:</span> <span class="n">headers</span><span class="p">,</span> <span class="ss">status_code</span><span class="p">:</span> <span class="mi">200</span> <span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="n">body</span> <span class="o">=</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">Mangafox</span><span class="o">.</span><span class="n">gunzip</span><span class="p">(</span><span class="n">body</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="n">fetch_pages</span><span class="p">(</span><span class="n">chapter_link</span><span class="p">,</span> <span class="n">body</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="n">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span> <span class="ss">:err</span><span class="p">,</span> <span class="s2">&#34;not found&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>(Listing 1.1)</p>
<p>It calls &ldquo;<tt>HTTPotion.get/2</tt>&rdquo; sending a bunch of HTTP options and receives a &ldquo;<tt>%HTTPotion.Response</tt>&rdquo; struct that is then decomposed to get the body and headers. It gunzips the body if necessary and goes to parse the HTML itself.</p>
<p>Similar code exists in 6 different modules, with different links and different parser functions. It&rsquo;s a lot of repetition, but what about making the above code look like the snippet below?</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">Mangafox</span><span class="o">.</span><span class="n">ChapterPage</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="nb">require</span> <span class="no">Logger</span>
</span></span><span class="line"><span class="cl">  <span class="nb">require</span> <span class="no">ExMangaDownloadr</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">pages</span><span class="p">(</span><span class="n">chapter_link</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">fetch</span> <span class="n">chapter_link</span><span class="p">,</span> <span class="k">do</span><span class="p">:</span> <span class="n">fetch_pages</span><span class="p">(</span><span class="n">chapter_link</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>(Listing 1.2)</p>
<p>Changed 9 lines to just 1. And by the way, this same line can be written like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">fetch</span> <span class="n">chapter_link</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">fetch_pages</span><span class="p">(</span><span class="n">chapter_link</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Seems familiar? It&rsquo;s like every block in the Elixir language, you can write it in the &ldquo;do/end&rdquo; block format or the way it really is under the covers: a keyword list with a key named &ldquo;:do&rdquo;. And the way this macro is defined is like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMangaDownloadr</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="n">defmacro</span> <span class="n">fetch</span><span class="p">(</span><span class="n">link</span><span class="p">,</span> <span class="k">do</span><span class="p">:</span> <span class="n">expression</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">quote</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&#34;Fetching from </span><span class="si">#{</span><span class="n">unquote</span><span class="p">(</span><span class="n">link</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">case</span> <span class="no">HTTPotion</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">unquote</span><span class="p">(</span><span class="n">link</span><span class="p">),</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">http_headers</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="o">%</span><span class="no">HTTPotion</span><span class="o">.</span><span class="n">Response</span><span class="p">{</span> <span class="ss">body</span><span class="p">:</span> <span class="n">body</span><span class="p">,</span> <span class="ss">headers</span><span class="p">:</span> <span class="n">headers</span><span class="p">,</span> <span class="ss">status_code</span><span class="p">:</span> <span class="mi">200</span> <span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="n">body</span> <span class="o">|&gt;</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">gunzip</span><span class="p">(</span><span class="n">headers</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="n">unquote</span><span class="p">(</span><span class="n">expression</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span> <span class="ss">:err</span><span class="p">,</span> <span class="s2">&#34;not found&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>(Listing 1.3)</p>
<p>There are a lot of details to consider when writing a macro and I recommend reading the documentation on <a href="http://elixir-lang.org/getting-started/meta/macros.html"target="_blank" rel="noopener">Macros</a>. The code is basically copying the function body from the &ldquo;<tt>ChapterPage.pages/1</tt>&rdquo; (Listing 1.1) and pasting it into the &ldquo;<tt>quote do .. end</tt>&rdquo; block (Listing 1.3).</p>
<p>Inside that code we have &ldquo;<tt>unquote(link)</tt>&rdquo; and &ldquo;<tt>unquote(expression)</tt>&rdquo;. You also must read the documentation on <a href="http://elixir-lang.org/getting-started/meta/quote-and-unquote.html"target="_blank" rel="noopener">&ldquo;Quote and Unquote&rdquo;</a>. It just expands this &ldquo;external&rdquo; code inside the macro code to defer execution until the macro quote code is actually executed, instead of running it at that exact moment. I know, tricky to wrap your head around this the first time.</p>
<p>The bottom line is: whatever code is inside the &ldquo;quote&rdquo; block will be &ldquo;inserted&rdquo; where we called &ldquo;<tt>ExMangaDownloadr.fetch/2</tt>&rdquo; in the &ldquo;pages/1&rdquo; function in Listing 1.2, together with the unquoted code you passed as a parameter.</p>
<p>The resulting code will resemble the original code in Listing 1.1.</p>
<p>To make it simpler, if you were in Javascript this would be similar code:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">eval</span><span class="p">(</span><span class="s2">&#34;doSomething(&#39;&#34;</span> <span class="o">+</span> <span class="nx">url</span> <span class="o">+</span> <span class="s2">&#34;&#39;)&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">pages</span><span class="p">(</span><span class="nx">page_link</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fetch</span><span class="p">(</span><span class="nx">page_link</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>&ldquo;Quote&rdquo; would be like the string body in an eval and &ldquo;unquote&rdquo; just concatenating the value you passed inside the code being eval-ed. This is a crude metaphor as &ldquo;quote/unquote&rdquo; is <strong>way</strong> more powerful and cleaner than ugly &ldquo;eval&rdquo; (which you shouldn&rsquo;t be using, by the way!) But this metaphor should do to make you understand the code above.</p>
<p>Another place I used a macro was to save the images list in a dump file and load it later if the tool crashes for some reason, in order not to have to start over from scratch. The original code was like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">dump_file</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">directory</span><span class="si">}</span><span class="s2">/images_list.dump&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">images_list</span> <span class="o">=</span> <span class="k">if</span> <span class="no">File</span><span class="o">.</span><span class="n">exists?</span><span class="p">(</span><span class="n">dump_file</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">                <span class="ss">:erlang</span><span class="o">.</span><span class="n">binary_to_term</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">read!</span><span class="p">(</span><span class="n">dump_file</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">              <span class="k">else</span>
</span></span><span class="line"><span class="cl">                <span class="n">list</span> <span class="o">=</span> <span class="o">[</span><span class="n">url</span><span class="p">,</span> <span class="n">source</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">                  <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">chapters</span>
</span></span><span class="line"><span class="cl">                  <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">pages</span>
</span></span><span class="line"><span class="cl">                  <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">images_sources</span>
</span></span><span class="line"><span class="cl">                <span class="no">File</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">dump_file</span><span class="p">,</span> <span class="ss">:erlang</span><span class="o">.</span><span class="n">term_to_binary</span><span class="p">(</span><span class="n">list</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">                <span class="n">list</span>
</span></span><span class="line"><span class="cl">              <span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>(Listing 1.4)</p>
<p>And now that you understand macros, you will understand what I did here:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMangaDownloadr</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="n">defmacro</span> <span class="n">managed_dump</span><span class="p">(</span><span class="n">directory</span><span class="p">,</span> <span class="k">do</span><span class="p">:</span> <span class="n">expression</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">quote</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">dump_file</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">unquote</span><span class="p">(</span><span class="n">directory</span><span class="p">)</span><span class="si">}</span><span class="s2">/images_list.dump&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">images_list</span> <span class="o">=</span> <span class="k">if</span> <span class="no">File</span><span class="o">.</span><span class="n">exists?</span><span class="p">(</span><span class="n">dump_file</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">          <span class="ss">:erlang</span><span class="o">.</span><span class="n">binary_to_term</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">read!</span><span class="p">(</span><span class="n">dump_file</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span>
</span></span><span class="line"><span class="cl">          <span class="n">list</span> <span class="o">=</span> <span class="n">unquote</span><span class="p">(</span><span class="n">expression</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="no">File</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">dump_file</span><span class="p">,</span> <span class="ss">:erlang</span><span class="o">.</span><span class="n">term_to_binary</span><span class="p">(</span><span class="n">list</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">          <span class="n">list</span>
</span></span><span class="line"><span class="cl">        <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">CLI</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">Workflow</span>
</span></span><span class="line"><span class="cl">  <span class="nb">require</span> <span class="no">ExMangaDownloadr</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">process</span><span class="p">(</span><span class="n">manga_name</span><span class="p">,</span> <span class="n">directory</span><span class="p">,</span> <span class="p">{</span><span class="n">_url</span><span class="p">,</span> <span class="n">_source</span><span class="p">}</span> <span class="o">=</span> <span class="n">manga_site</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">File</span><span class="o">.</span><span class="n">mkdir_p!</span><span class="p">(</span><span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">images_list</span> <span class="o">=</span> 
</span></span><span class="line"><span class="cl">      <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">managed_dump</span> <span class="n">directory</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="n">manga_site</span>
</span></span><span class="line"><span class="cl">          <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">chapters</span>
</span></span><span class="line"><span class="cl">          <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">pages</span>
</span></span><span class="line"><span class="cl">          <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">images_sources</span> 
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And there you have it! And now you see how &ldquo;do .. end&rdquo; blocks are implemented. It just passes the expression as the value in the keyword list of the macro definition. Let&rsquo;s define a dumb macro:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Foo</span>
</span></span><span class="line"><span class="cl">  <span class="n">defmacro</span> <span class="n">foo</span><span class="p">(</span><span class="k">do</span><span class="p">:</span> <span class="n">expression</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">     <span class="n">quote</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">       <span class="n">unquote</span><span class="p">(</span><span class="n">expression</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And now the following calls are all equivalent:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="no">Foo</span>
</span></span><span class="line"><span class="cl"><span class="no">Foo</span><span class="o">.</span><span class="n">foo</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">IO</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="no">Foo</span><span class="o">.</span><span class="n">foo</span> <span class="k">do</span><span class="p">:</span> <span class="no">IO</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="no">Foo</span><span class="o">.</span><span class="n">foo</span><span class="p">(</span><span class="k">do</span><span class="p">:</span> <span class="no">IO</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="no">Foo</span><span class="o">.</span><span class="n">foo</span><span class="p">(</span><span class="o">[</span><span class="k">do</span><span class="p">:</span> <span class="no">IO</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="no">Foo</span><span class="o">.</span><span class="n">foo</span><span class="p">(</span><span class="o">[</span><span class="p">{</span><span class="ss">:do</span><span class="p">,</span> <span class="no">IO</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="mi">1</span><span class="p">)}</span><span class="o">]</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is macros combined with <a href="http://elixir-lang.org/getting-started/maps-and-dicts.html"target="_blank" rel="noopener">Keyword Lists</a> which I explained in previous articles and it&rsquo;s simply a List with tuples where each tuple has an atom key and a value.</p>
<h3>More Macros<span class="hx:absolute hx:-mt-20" id="more-macros"></span>
    <a href="#more-macros" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Another opportunity to refactor was the &ldquo;mangareader.ex&rdquo; and &ldquo;mangafox.ex&rdquo; modules that were just used in the unit tests &ldquo;mangareader_test.ex&rdquo; and &ldquo;mangafox_test.ex&rdquo;. This is the old &ldquo;mangareader.ex&rdquo; code:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">defmacro</span> <span class="n">__using__</span><span class="p">(</span><span class="n">_opts</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">quote</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="k">alias</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span><span class="o">.</span><span class="n">IndexPage</span>
</span></span><span class="line"><span class="cl">      <span class="k">alias</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span><span class="o">.</span><span class="n">ChapterPage</span>
</span></span><span class="line"><span class="cl">      <span class="k">alias</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span><span class="o">.</span><span class="n">Page</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is how it was used in &ldquo;mangareader_test.ex&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReaderTest</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExUnit</span><span class="o">.</span><span class="n">Case</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It was just a shortcut to alias the modules in order to use them directly inside the tests. I just moved the entire module as a macro in &ldquo;ex_manga_downloadr.ex&rdquo; module:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">mangareader</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">quote</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="k">alias</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span><span class="o">.</span><span class="n">IndexPage</span>
</span></span><span class="line"><span class="cl">      <span class="k">alias</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span><span class="o">.</span><span class="n">ChapterPage</span>
</span></span><span class="line"><span class="cl">      <span class="k">alias</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span><span class="o">.</span><span class="n">Page</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">mangafox</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defmacro</span> <span class="n">__using__</span><span class="p">(</span><span class="n">which</span><span class="p">)</span> <span class="k">when</span> <span class="n">is_atom</span><span class="p">(</span><span class="n">which</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">apply</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="n">which</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And now I can use it like this in the test file:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReaderTest</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExUnit</span><span class="o">.</span><span class="n">Case</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExMangaDownloadr</span><span class="p">,</span> <span class="ss">:mangareader</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The special &ldquo;<strong>using</strong>&rdquo; macro is called when I &ldquo;use&rdquo; a module, and I can even pass arguments to it. The implementation then uses &ldquo;apply/3&rdquo; to dynamically call the correct macro. This is exactly how Phoenix imports the proper behaviors for Models, Views, Controllers, Router, for example:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">PageController</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:controller</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The macros are open in a Phoenix file and available in the &ldquo;web/web.ex&rdquo; module, so I just copied the same behavior. And now I have 2 fewer files to worry about.</p>
<h3>Tiny refactorings<span class="hx:absolute hx:-mt-20" id="tiny-refactorings"></span>
    <a href="#tiny-refactorings" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In the previous code I used the &ldquo;<tt>String.to_atom/1</tt>&rdquo; to convert the string of the module name to an atom, to be later used in &ldquo;apply/3&rdquo; calls:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">manga_source</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">module</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="n">source</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;mangareader&#34;</span> <span class="o">-&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">to_atom</span><span class="p">(</span><span class="s2">&#34;Elixir.ExMangaDownloadr.MangaReader.</span><span class="si">#{</span><span class="n">module</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;mangafox&#34;</span>    <span class="o">-&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">to_atom</span><span class="p">(</span><span class="s2">&#34;Elixir.ExMangaDownloadr.Mangafox.</span><span class="si">#{</span><span class="n">module</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I changed it to this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl">    <span class="s2">&#34;mangareader&#34;</span> <span class="o">-&gt;</span> <span class="ss">:&#34;Elixir.ExMangaDownloadr.MangaReader.</span><span class="si">#{</span><span class="n">module</span><span class="si">}</span><span class="ss">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;mangafox&#34;</span>    <span class="o">-&gt;</span> <span class="ss">:&#34;Elixir.ExMangaDownloadr.Mangafox.</span><span class="si">#{</span><span class="n">module</span><span class="si">}</span><span class="ss">&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s just a shortcut to do the same thing.</p>
<p>In the parser I was also not using <a href="https://github.com/philss/floki"target="_blank" rel="noopener">Floki</a> correctly. So take a look at this piece of old code:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">fetch_manga_title</span><span class="p">(</span><span class="n">html</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Floki</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">html</span><span class="p">,</span> <span class="s2">&#34;#mangaproperties h1&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">fn</span> <span class="p">{</span><span class="s2">&#34;h1&#34;</span><span class="p">,</span> <span class="o">[]</span><span class="p">,</span> <span class="o">[</span><span class="n">title</span><span class="o">]</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">title</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">at</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">fetch_chapters</span><span class="p">(</span><span class="n">html</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Floki</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">html</span><span class="p">,</span> <span class="s2">&#34;#listing a&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span> <span class="n">fn</span> <span class="p">{</span><span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="o">[</span><span class="p">{</span><span class="s2">&#34;href&#34;</span><span class="p">,</span> <span class="n">url</span><span class="p">}</span><span class="o">]</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">url</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now using the better helper functions that Floki provides:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">fetch_manga_title</span><span class="p">(</span><span class="n">html</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">html</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Floki</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s2">&#34;#mangaproperties h1&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Floki</span><span class="o">.</span><span class="n">text</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">fetch_chapters</span><span class="p">(</span><span class="n">html</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">html</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Floki</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s2">&#34;#listing a&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Floki</span><span class="o">.</span><span class="n">attribute</span><span class="p">(</span><span class="s2">&#34;href&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This was a case of not reading the documentation as I should have. Much cleaner!</p>
<p>I did other bits of cleanup but I think this should cover the major changes. And finally, I bumped up the version to &ldquo;1.0.0&rdquo; as well!</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl">   <span class="k">def</span> <span class="nf">project</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">     <span class="o">[</span><span class="ss">app</span><span class="p">:</span> <span class="ss">:ex_manga_downloadr</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>     <span class="ss">version</span><span class="p">:</span> <span class="s2">&#34;0.0.1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>     <span class="ss">version</span><span class="p">:</span> <span class="s2">&#34;1.0.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">elixir</span><span class="p">:</span> <span class="s2">&#34;~&gt; 1.1&#34;</span><span class="p">,</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And speaking of versions, I&rsquo;m using Elixir 1.1 but pay attention as <a href="https://github.com/elixir-lang/elixir/blob/ef5ba3af059f76489631dc26b52ecaeff09af3fe/CHANGELOG.md"target="_blank" rel="noopener">Elixir 1.2</a> is just around the corner and it brings some niceties. For example, that macro that aliased a few modules could be written this way now:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">mangareader</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">quote</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">alias</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span><span class="o">.</span><span class="p">{</span><span class="no">IndexPage</span><span class="p">,</span> <span class="no">ChapterPage</span><span class="p">,</span> <span class="no">Page</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is just 1 feature among many other syntax improvements and support for the newest <a href="http://www.erlang.org/news/88"target="_blank" rel="noopener">Erlang R18</a>. Keep an eye on both!</p>
]]></content:encoded><category>elixir-and-erlang</category><category>software-engineering</category><category>testing</category></item><item><title>The Obligatory "Flame War" Phoenix vs Node.js</title><link>https://akitaonrails.github.io/en/2015/12/03/the-obligatory-flame-war-phoenix-vs-node-js/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/03/the-obligatory-flame-war-phoenix-vs-node-js/</guid><pubDate>Thu, 03 Dec 2015 12:01:00 -0200</pubDate><description>&lt;p&gt;I&amp;rsquo;ll warn you upfront: this will be a very unfair post. Not only am I biased for disliking Javascript and Node.js, but at this moment, I am very excited and fascinated by Elixir and Erlang.&lt;/p&gt;
&lt;p&gt;Comparisons are always unfair. There is no such thing as a &amp;ldquo;fair&amp;rdquo; synthetic benchmark comparison, the author is &lt;strong&gt;always&lt;/strong&gt; biased towards some targeted result. It&amp;rsquo;s the old pseudo-science case of having a conclusion and picking and choosing data that backs those conclusions. There are just too many different variables. People think it&amp;rsquo;s fair when you run it in the same machine against 2 &amp;ldquo;similar&amp;rdquo; kinds of applications, but it is not. Please don&amp;rsquo;t trust me on this as well, do your own tests.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I&rsquo;ll warn you upfront: this will be a very unfair post. Not only am I biased for disliking Javascript and Node.js, but at this moment, I am very excited and fascinated by Elixir and Erlang.</p>
<p>Comparisons are always unfair. There is no such thing as a &ldquo;fair&rdquo; synthetic benchmark comparison, the author is <strong>always</strong> biased towards some targeted result. It&rsquo;s the old pseudo-science case of having a conclusion and picking and choosing data that backs those conclusions. There are just too many different variables. People think it&rsquo;s fair when you run it in the same machine against 2 &ldquo;similar&rdquo; kinds of applications, but it is not. Please don&rsquo;t trust me on this as well, do your own tests.</p>
<p>All that being said, let&rsquo;s have some fun, shall we?</p>
<h3>The Obligatory &ldquo;Hello World&rdquo; warm-up<span class="hx:absolute hx:-mt-20" id="the-obligatory-hello-world-warm-up"></span>
    <a href="#the-obligatory-hello-world-warm-up" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>For this very short post, I just created a Node.js + Express &ldquo;hello world,&rdquo; I will point to its root endpoint, Express, rendering a super simple HTML template with one title and one paragraph.</p>
<p>For Elixir, I just bootstrapped a bare-bone Phoenix project and added one extra endpoint called &ldquo;/teste&rdquo; in the Router. This endpoint will call the PageController, then the &ldquo;teste&rdquo; function, and render an EEX template with the same title and paragraph as in the Express example.</p>
<p>Simple enough. Phoenix does more than Express, but this is not supposed to be a fair trial anyway. I chose <a href="https://www.joedog.org/siege-home/"target="_blank" rel="noopener">Siege</a> as the testing tool for no particular reason. You can pick the testing tool you like the most. I am running this over my 2013 Macbook Pro with 8 cores and 16GB of RAM, so this benchmark will never max out my machine.</p>
<p>The first test is a simple run of 200 concurrent connections (the number of CPUs I have) firing just 8 requests each for 1,600. First, the Node.js + Express results:</p>
<video controls>
<source src="https://s3.us-east-2.amazonaws.com/blip.tv/Elixir/Node+8+x+200.mp4">
Your browser does not support the video tag. [Direct Link](https://s3.us-east-2.amazonaws.com/blip.tv/Elixir/Node+8+x+200.mp4)
</source></video>
<p>The first run already broke a few connections, but the 2nd run picked up and finished all 1,600 requests. And this is the Phoenix results:</p>
<video controls>
<source src="https://s3.us-east-2.amazonaws.com/blip.tv/Elixir/Phoenix+8+x+200.mp4">
Your browser does not support the video tag. [Direct Link](https://s3.us-east-2.amazonaws.com/blip.tv/Elixir/Phoenix+8+x+200.mp4)
</source></video>
<p>As you can see, Node.js has the upper hand in terms of the total time spent. One single Node.js process can only run one single real OS thread. Therefore it had to use just a single CPU core (although I had 7 other cores to spare). On the other hand, Elixir can reach all 8 cores of my machine, each running a Scheduler in a single real OS thread. So, if this test was CPU bound, it should have run 8 times faster than the Node.js version. As the test is largely a case of I/O bound operations, the clever async construction of Node.js does the job just fine.</p>
<p>This is not an impressive test by any stretch of the imagination. But we&rsquo;re just warming up.</p>
<p>Oh, and by the way, notice how Phoenix logs show the request processing times in MICROseconds instead of milliseconds!</p>
<h3>The Real Fun<span class="hx:absolute hx:-mt-20" id="the-real-fun"></span>
    <a href="#the-real-fun" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Now comes the real fun. In this second run, I added a blocking &ldquo;sleep&rdquo; call to both projects, so each request will sleep for one full second, and this is not absurd. Many programmers will use poor code that blocks for that time, process too much data from the database, render templates that are too complex, and so on. Never trust a programmer to follow best practices all the time.</p>
<p>Then, I fire up Siege with ten concurrent connections and just one request each, for starters.</p>
<video controls>
<source src="https://s3.us-east-2.amazonaws.com/blip.tv/Elixir/Node+1+x+10+Sleep.mp4">
Your browser does not support the video tag. [Direct Link](https://s3.us-east-2.amazonaws.com/blip.tv/Elixir/Node+1+x+10+Sleep.mp4)
</source></video>
<p>This is why in my previous article &ldquo;Why Elixir?&rdquo; (<a href="http://www.akitaonrails.com/en/2015/12/01/o-obrigatorio-por-que-elixir-visao-pessoal%29"target="_blank" rel="noopener">http://www.akitaonrails.com/en/2015/12/01/o-obrigatorio-por-que-elixir-visao-pessoal)</a>, I repeated many times how <strong>&ldquo;rudimentary&rdquo;</strong> a Reactor pattern-based solution is. It is super easy to block a single-threaded event loop.</p>
<p>If you didn&rsquo;t know that already, how does Node.js work? In summary, it is a simple infinite loop. When a Javascript function runs, it blocks that event loop. The function has to explicitly yield control back to the loop for another function to have a chance to run. I/O calls take time and just sit back idly waiting for a response, so it can yield control back and wait for a callback to continue running, which is why you end up with the dreaded <em>&ldquo;callback pyramid hell&rdquo;</em>.</p>
<p>Now, with what I explained in all my previous articles, you may already know how Elixir + Phoenix will perform:</p>
<video controls>
<source src="https://s3.us-east-2.amazonaws.com/blip.tv/Elixir/Phoenix+2+x+400+-+Sleep.mp4">
Your browser does not support the video tag. [Direct Link](https://s3.us-east-2.amazonaws.com/blip.tv/Elixir/Phoenix+2+x+400+-+Sleep.mp4)
</source></video>
<p>As expected, this is a walk in the park for Phoenix. It doesn&rsquo;t have a rudimentary single-thread loop waiting for the running functions to yield control back willingly. The Schedulers can forcefully suspend running coroutines/processes if they think they are taking too much time (the 2,000 reductions count and priority configurations), so every running process has a fair share of resources to run.</p>
<p>Because of that, I can keep increasing the number of requests and concurrent connections, and it&rsquo;s still <strong>fast</strong>.</p>
<p>In Node.js, if a function takes 1 second to run, it blocks the loop. When it finally returns, the next 1-second function can run. That&rsquo;s why if I have ten requests taking 1 second each to run, the entire process will linearly take 10 seconds!</p>
<p>Which obviously does not scale! If you &ldquo;do it right,&rdquo; you can scale. But why bother?</p>
<h3>&ldquo;Node&rdquo; done right<span class="hx:absolute hx:-mt-20" id="node-done-right"></span>
    <a href="#node-done-right" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>As a side note, I find it ironic that &ldquo;Node&rdquo; is called &ldquo;Node.&rdquo; I would assume that connecting multiple Nodes that communicate with each other should be easy. And as a matter of fact, <a href="http://www.sitepoint.com/how-to-create-a-node-js-cluster-for-speeding-up-your-apps/"target="_blank" rel="noopener">it is not</a>.</p>
<p>If I had spun up 5 Node processes, instead of 10 seconds, everything would take 2 seconds as five requests would block the 5 Node processes for 1 second, and when returned, the next five requests would block again. This is similar to what we need to do with Ruby or Python, which have the dreaded big Global Interpreter Locks (GIL) that, in reality, can only run one blocking computation at a time. (Ruby with Eventmachine and Python with Tornado or Twisted are similar to Node.js implementation of a reactor event loop).</p>
<p>Elixir can do much better in terms of actually coordinating different nodes, and it is the Erlang underpinnings that allow highly distributed systems such as <a href="https://www.ejabberd.im/"target="_blank" rel="noopener">ejabberd</a> or <a href="https://www.rabbitmq.com/"target="_blank" rel="noopener">RabbitMQ</a> to do their thing as efficiently as they can.</p>
<p>Check out how simple it is for one Elixir Node to notice the presence of other Elixir nodes and make them send and receive messages between each other:</p>
<video controls>
<source src="https://s3.us-east-2.amazonaws.com/blip.tv/Elixir/7+-+Nodes.mp4">
Your browser does not support the video tag. [Direct Link](https://s3.us-east-2.amazonaws.com/blip.tv/Elixir/7+-+Nodes.mp4)
</source></video>
<p>Yep, it is this simple. We have been using Remote Procedure Calls (RPC) for decades; this is not new. Erlang has implemented this for years, and it is built-in and available for easy usage out-of-the-box.</p>
<p>On their websites, ejabberd calls itself a &ldquo;Robust, Scalable, and Extensible XMPP Server,&rdquo; and RabbitMQ calls itself &ldquo;Robust messaging for applications.&rdquo; Now we know they deserve the labels &ldquo;Robust&rdquo; and &ldquo;Scalable.&rdquo;</p>
<p>So, we are struggling to do things that have been polished and ready for years. Elixir is the key to unlocking all this Erlang goodness right now. Let&rsquo;s just use it and stop shrugging.</p>
]]></content:encoded><category>performance</category><category>elixir-and-erlang</category><category>javascript</category></item><item><title>Ex Manga Downloadr - Part 3: Mangafox Support!</title><link>https://akitaonrails.github.io/en/2015/12/02/ex-manga-downloadr-part-3-mangafox-support/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/02/ex-manga-downloadr-part-3-mangafox-support/</guid><pubDate>Wed, 02 Dec 2015 16:27:00 -0200</pubDate><description>&lt;p&gt;I thought &lt;a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener"&gt;Part 2&lt;/a&gt; would be my last article about this tool, but turns out it&amp;rsquo;s just too much fun to let it go easily. As usual, all the source code is on my &lt;a href="https://github.com/akitaonrails/ex_manga_downloadr"target="_blank" rel="noopener"&gt;Github repository&lt;/a&gt;. And the gist of the post is that now you can do this:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;git pull
mix escript.build
./ex_manga_downloadr -n onepunch -u http://mangafox.me/manga/onepunch_man/ -d /tmp/onepunch -s mangafox&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;And there you go: download from Mangafox built-in! \o/&lt;/p&gt;</description><content:encoded><![CDATA[<p>I thought <a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener">Part 2</a> would be my last article about this tool, but turns out it&rsquo;s just too much fun to let it go easily. As usual, all the source code is on my <a href="https://github.com/akitaonrails/ex_manga_downloadr"target="_blank" rel="noopener">Github repository</a>. And the gist of the post is that now you can do this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>git pull
mix escript.build
./ex_manga_downloadr -n onepunch -u http://mangafox.me/manga/onepunch_man/ -d /tmp/onepunch -s mangafox</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And there you go: download from Mangafox built-in! \o/</p>
<p>It starts when I wanted to download manga that is not available at MangaReader but exists under Mangafox.</p>
<p>So, the initial endeavor was to copy the MangaReader parser modules (IndexPage, ChapterPage, and Page) and paste them at a specific &ldquo;lib/ex_manga_downloadr/mangafox&rdquo; folder. Same thing done to the unit tests folder. Just a matter of copying and pasting the files and change the &ldquo;MangaReader&rdquo; module name to &ldquo;Mangafox&rdquo;.</p>
<p>Of course the URL formats are different, the Floki CSS selectors are a bit different, so that&rsquo;s what had to change in the parser. For example, this is how I parse the chapter links from the main page at MangaReader:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">fetch_chapters</span><span class="p">(</span><span class="n">html</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Floki</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">html</span><span class="p">,</span> <span class="s2">&#34;#listing a&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span> <span class="n">fn</span> <span class="p">{</span><span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="o">[</span><span class="p">{</span><span class="s2">&#34;href&#34;</span><span class="p">,</span> <span class="n">url</span><span class="p">}</span><span class="o">]</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">url</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is the same thing but for Mangafox:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">fetch_chapters</span><span class="p">(</span><span class="n">html</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">html</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Floki</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s2">&#34;.chlist a[class=&#39;tips&#39;]&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span> <span class="n">fn</span> <span class="p">{</span><span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="o">[</span><span class="p">{</span><span class="s2">&#34;href&#34;</span><span class="p">,</span> <span class="n">url</span><span class="p">},</span> <span class="p">{</span><span class="s2">&#34;title&#34;</span><span class="p">,</span> <span class="n">_</span><span class="p">},</span> <span class="p">{</span><span class="s2">&#34;class&#34;</span><span class="p">,</span> <span class="s2">&#34;tips&#34;</span><span class="p">}</span><span class="o">]</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">url</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Exactly the same logic but the pattern matching structure is different because the returning HTML DOM nodes are different.</p>
<p>Another difference is that MangaReader returns everything in plain text by default, but Mangafox returns everything Gzipped regardless of whether I send the &ldquo;Accept-Encoding&rdquo; HTTP header (curiously, if I retry several times it changes behavior and sometimes sends plain text).</p>
<p>What I did differently was to check if the returned <tt>%HTTPotion.Response{}</tt> structure had a &ldquo;Content-Encoding&rdquo; header set to &ldquo;gzip&rdquo; and if so, gunzip it using the built-in Erlang &ldquo;zlib&rdquo; package (nothing to import!):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">gunzip</span><span class="p">(</span><span class="n">body</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="n">headers</span><span class="o">[</span><span class="ss">:&#34;Content-Encoding&#34;</span><span class="o">]</span> <span class="o">==</span> <span class="s2">&#34;gzip&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:zlib</span><span class="o">.</span><span class="n">gunzip</span><span class="p">(</span><span class="n">body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="n">body</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I would&rsquo;ve preferred if HTTPotion did that out of the box for me (#OpportunityToContribute!), but this was easy enough.</p>
<p>Once the unit tests were passing correctly after tuning the scrapper (HTTPotion requests) and parser (Floki selectors) it was time to make my Worker aware of the existence of this new set of modules.</p>
<p>The Workflow module just calls the Worker, which in turn does the heavy lifting of fetching pages and downloading images. The Worker called the MangaReader module directly, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">PoolManagement</span><span class="o">.</span><span class="n">Worker</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">GenServer</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span>
</span></span><span class="line"><span class="cl">  <span class="nb">require</span> <span class="no">Logger</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">chapter_page</span><span class="p">(</span><span class="n">chapter_link</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">Task</span><span class="o">.</span><span class="n">async</span> <span class="n">fn</span> <span class="o">-&gt;</span> 
</span></span><span class="line"><span class="cl">      <span class="ss">:poolboy</span><span class="o">.</span><span class="n">transaction</span> <span class="ss">:worker_pool</span><span class="p">,</span> <span class="n">fn</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="p">{</span><span class="ss">:chapter_page</span><span class="p">,</span> <span class="n">chapter_link</span><span class="p">},</span> <span class="vi">@timeout_ms</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span><span class="p">,</span> <span class="vi">@transaction_timeout_ms</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_call</span><span class="p">({</span><span class="ss">:chapter_page</span><span class="p">,</span> <span class="n">chapter_link</span><span class="p">},</span> <span class="n">_from</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="no">ChapterPages</span><span class="o">.</span><span class="n">pages</span><span class="p">(</span><span class="n">chapter_link</span><span class="p">),</span> <span class="n">state</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>That &ldquo;<tt>use ExMangaDownloadr.MangaReader</tt>&rdquo; statement up above is just a macro that will alias the corresponding modules:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">defmacro</span> <span class="n">__using__</span><span class="p">(</span><span class="n">_opts</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">quote</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="k">alias</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span><span class="o">.</span><span class="n">IndexPage</span>
</span></span><span class="line"><span class="cl">      <span class="k">alias</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span><span class="o">.</span><span class="n">ChapterPage</span>
</span></span><span class="line"><span class="cl">      <span class="k">alias</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span><span class="o">.</span><span class="n">Page</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So when I call &ldquo;<tt>ChapterPages.pages(chapter_link)</tt>&rdquo; it&rsquo;s a shortcut to use the fully qualified module name like this: &ldquo;<tt>ExMangaDownloadr.MangaReader.ChapterPages.pages(chapter_link)</tt>&rdquo;.</p>
<p>An Elixir module namespace is just an Atom. Nested module names have the full, dot-separated name, prefixed with its parent. For example:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Foo</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">defmodule</span> <span class="no">Bar</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">defmodule</span> <span class="no">Xyz</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">       <span class="k">def</span> <span class="nf">teste</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">       <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You can just call &ldquo;<tt>Foo.Bar.Xyz.teste()</tt>&rdquo; and that&rsquo;s it. But there is a small trick. Elixir also transparently prefixes the full module name with &ldquo;Elixir&rdquo;. So in reality, the full module name is &ldquo;Elixir.Foo.Bar.Xyz&rdquo;, in order to make sure no Elixir module ever conflicts with an existing Erlang module.</p>
<p>This is important because of this new function I added to the Worker module first:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">manga_source</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">module</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="n">source</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;mangareader&#34;</span> <span class="o">-&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">to_atom</span><span class="p">(</span><span class="s2">&#34;Elixir.ExMangaDownloadr.MangaReader.</span><span class="si">#{</span><span class="n">module</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;mangafox&#34;</span>    <span class="o">-&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">to_atom</span><span class="p">(</span><span class="s2">&#34;Elixir.ExMangaDownloadr.Mangafox.</span><span class="si">#{</span><span class="n">module</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is how I map from &ldquo;mangafox&rdquo; to the new &ldquo;ExMangaDownloadr.Mangafox.&rdquo; namespace. And because of the dynamic, message passing nature of Elixir, I can replace this code:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">handle_call</span><span class="p">({</span><span class="ss">:chapter_page</span><span class="p">,</span> <span class="n">chapter_link</span><span class="p">},</span> <span class="n">_from</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="no">ChapterPages</span><span class="o">.</span><span class="n">pages</span><span class="p">(</span><span class="n">chapter_link</span><span class="p">),</span> <span class="n">state</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>With this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">handle_call</span><span class="p">({</span><span class="ss">:chapter_page</span><span class="p">,</span> <span class="n">chapter_link</span><span class="p">,</span> <span class="n">source</span><span class="p">},</span> <span class="n">_from</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">links</span> <span class="o">=</span> <span class="n">source</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">manga_source</span><span class="p">(</span><span class="s2">&#34;ChapterPage&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">apply</span><span class="p">(</span><span class="ss">:pages</span><span class="p">,</span> <span class="o">[</span><span class="n">chapter_link</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="n">links</span><span class="p">,</span> <span class="n">state</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I can now choose between the &ldquo;Elixir.ExMangaDownloadr.Mangafox.ChapterPage&rdquo; or &ldquo;Elixir.ExMangaDownloadr.MangaReader.ChapterPage&rdquo; modules, call the <tt>pages/1</tt> function and send the same argument as before. I just have to make sure I can receive a &ldquo;source&rdquo; string from the command line now, so I change the CLI module like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">parse_args</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">parse</span> <span class="o">=</span> <span class="no">OptionParser</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">args</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">switches</span><span class="p">:</span> <span class="o">[</span><span class="nb">name</span><span class="p">:</span> <span class="ss">:string</span><span class="p">,</span> <span class="ss">url</span><span class="p">:</span> <span class="ss">:string</span><span class="p">,</span> <span class="ss">directory</span><span class="p">:</span> <span class="ss">:string</span><span class="p">,</span> <span class="ss">source</span><span class="p">:</span> <span class="ss">:string</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">aliases</span><span class="p">:</span> <span class="o">[</span><span class="ss">n</span><span class="p">:</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">u</span><span class="p">:</span> <span class="ss">:url</span><span class="p">,</span> <span class="ss">d</span><span class="p">:</span> <span class="ss">:directory</span><span class="p">,</span> <span class="ss">s</span><span class="p">:</span> <span class="ss">:source</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="n">parse</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="o">[</span><span class="nb">name</span><span class="p">:</span> <span class="n">manga_name</span><span class="p">,</span> <span class="ss">url</span><span class="p">:</span> <span class="n">url</span><span class="p">,</span> <span class="ss">directory</span><span class="p">:</span> <span class="n">directory</span><span class="p">,</span> <span class="ss">source</span><span class="p">:</span> <span class="n">source</span><span class="o">]</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">process</span><span class="p">(</span><span class="n">manga_name</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">directory</span><span class="p">,</span> <span class="n">source</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="o">[</span><span class="nb">name</span><span class="p">:</span> <span class="n">manga_name</span><span class="p">,</span> <span class="ss">directory</span><span class="p">:</span> <span class="n">directory</span><span class="o">]</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">process</span><span class="p">(</span><span class="n">manga_name</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="p">}</span> <span class="o">-&gt;</span> <span class="n">process</span><span class="p">(</span><span class="ss">:help</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Compared to the previous version I just added the &ldquo;<tt>:source</tt>&rdquo; string argument to the OptionParser and passed the captured value to <tt>process/4</tt>. I should add some validation here to avoid strings different than &ldquo;mangareader&rdquo; or &ldquo;mangafox&rdquo;, but I will leave that to another time.</p>
<p>And in the Workflow module, instead of starting from just the manga URL, now I have to start with both the URL and the manga source:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">[</span><span class="n">url</span><span class="p">,</span> <span class="n">source</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">chapters</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">pages</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">images_sources</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Which means that each of the above functions has to not only return the new URL lists but also pass through the source:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">chapters</span><span class="p">(</span><span class="o">[</span><span class="n">url</span><span class="p">,</span> <span class="n">source</span><span class="o">]</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">_manga_title</span><span class="p">,</span> <span class="n">chapter_list</span><span class="p">}</span> <span class="o">=</span> <span class="n">source</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Worker</span><span class="o">.</span><span class="n">manga_source</span><span class="p">(</span><span class="s2">&#34;IndexPage&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">apply</span><span class="p">(</span><span class="ss">:chapters</span><span class="p">,</span> <span class="o">[</span><span class="n">url</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">[</span><span class="n">chapter_list</span><span class="p">,</span> <span class="n">source</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This was the only function in the Workflow module hardcoded to MangaReader so I also made it dynamic using the same <tt>manga_source/2</tt> function from the Worker, and notice the return value being &ldquo;<tt>[chapter_list, source]</tt>&rdquo; instead of just &ldquo;<tt>chapter_list</tt>&rdquo;.</p>
<p>And now, I can finally test with &ldquo;<tt>mix test</tt>&rdquo; and create the new executable command line binary with &ldquo;<tt>mix escript.build</tt>&rdquo; and run the new version like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>./ex_manga_downloadr -n onepunch -u http://mangafox.me/manga/onepunch_man/ -d /tmp/onepunch -s mangafox</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The Mangafox site is very unreliable with several concurrent connections and it quickly times out sometimes, dumping ugly errors like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>15:58:46.637 [error] Task #PID&lt;0.2367.0&gt; started from #PID&lt;0.124.0&gt; terminating
** (stop) exited in: GenServer.call(#PID&lt;0.90.0&gt;, {:page_download_image, {&#34;http://z.mfcdn.net/store/manga/11362/TBD-053.2/compressed/h006.jpg&#34;, &#34;Onepunch-Man 53.2: 53rd Punch [Fighting Spirit] (2) at MangaFox.me-h006.jpg&#34;}, &#34;/tmp/onepunch&#34;}, 1000000)
    ** (EXIT) an exception was raised:
        ** (HTTPotion.HTTPError) connection_closing
            (httpotion) lib/httpotion.ex:209: HTTPotion.handle_response/1</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I haven&rsquo;t figured out how to retry HTTPotion requests properly yet. But one small thing I did was add an availability check in the Worker module. So you can just re-run the same command line and it will resume downloading only the remaining files:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">download_image</span><span class="p">({</span><span class="n">image_src</span><span class="p">,</span> <span class="n">image_filename</span><span class="p">},</span> <span class="n">directory</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">filename</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">directory</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="n">image_filename</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="no">File</span><span class="o">.</span><span class="n">exists?</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&#34;Image </span><span class="si">#{</span><span class="n">filename</span><span class="si">}</span><span class="s2"> already downloaded, skipping.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">image_src</span><span class="p">,</span> <span class="n">filename</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&#34;Downloading image </span><span class="si">#{</span><span class="n">image_src</span><span class="si">}</span><span class="s2"> to </span><span class="si">#{</span><span class="n">filename</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="no">HTTPotion</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">image_src</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="o">[</span><span class="ss">headers</span><span class="p">:</span> <span class="o">[</span><span class="s2">&#34;User-Agent&#34;</span><span class="p">:</span> <span class="vi">@user_agent</span><span class="o">]</span><span class="p">,</span> <span class="ss">timeout</span><span class="p">:</span> <span class="vi">@http_timeout</span><span class="o">]</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="o">%</span><span class="no">HTTPotion</span><span class="o">.</span><span class="n">Response</span><span class="p">{</span> <span class="ss">body</span><span class="p">:</span> <span class="n">body</span><span class="p">,</span> <span class="ss">headers</span><span class="p">:</span> <span class="n">_headers</span><span class="p">,</span> <span class="ss">status_code</span><span class="p">:</span> <span class="mi">200</span> <span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="no">File</span><span class="o">.</span><span class="n">write!</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">image_src</span><span class="p">,</span> <span class="n">filename</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="n">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="ss">:err</span><span class="p">,</span> <span class="n">image_src</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This should at least reduce rework. Another thing I am still working on is this other bit at the main &ldquo;CLI.process&rdquo; function:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">process</span><span class="p">(</span><span class="n">manga_name</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">directory</span><span class="p">,</span> <span class="n">source</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">File</span><span class="o">.</span><span class="n">mkdir_p!</span><span class="p">(</span><span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">dump_file</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">directory</span><span class="si">}</span><span class="s2">/images_list.dump&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">images_list</span> <span class="o">=</span> <span class="k">if</span> <span class="no">File</span><span class="o">.</span><span class="n">exists?</span><span class="p">(</span><span class="n">dump_file</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">                  <span class="ss">:erlang</span><span class="o">.</span><span class="n">binary_to_term</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">dump_file</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">                <span class="k">else</span>
</span></span><span class="line"><span class="cl">                  <span class="n">list</span> <span class="o">=</span> <span class="o">[</span><span class="n">url</span><span class="p">,</span> <span class="n">source</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">                    <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">chapters</span>
</span></span><span class="line"><span class="cl">                    <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">pages</span>
</span></span><span class="line"><span class="cl">                    <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">images_sources</span>
</span></span><span class="line"><span class="cl">                  <span class="no">File</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">dump_file</span><span class="p">,</span> <span class="ss">:erlang</span><span class="o">.</span><span class="n">term_to_binary</span><span class="p">(</span><span class="n">list</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">                  <span class="n">list</span>
</span></span><span class="line"><span class="cl">                <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">images_list</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">process_downloads</span><span class="p">(</span><span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">optimize_images</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">compile_pdfs</span><span class="p">(</span><span class="n">manga_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">finish_process</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As you can see the idea is to serialize the final images URL links to a file using the built-in serializer &ldquo;<tt>:erlang.binary_to_term/1</tt>&rdquo; and check if that dump file exists, and deserialize with &ldquo;<tt>:erlang.term_to_binary/1</tt>&rdquo; before fetching all pages all over again. Now the process can resume directly from the <tt>process_downloads/2</tt> function after.</p>
<p>Mangafox is terribly unreliable and I will need to figure out a better way to retry timed out connections without having to crash and manually restart from the command line. It&rsquo;s either a bad site or a clever one that shuts down scrapers like me, although I am guessing it&rsquo;s just bad infrastructure on their side.</p>
<p>If I downgrade from 50 processes to 5 in the pool, it seems to be able to handle it better (but the process slows down, of course):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl">    <span class="n">pool_options</span> <span class="o">=</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">      <span class="nb">name</span><span class="p">:</span> <span class="p">{</span><span class="ss">:local</span><span class="p">,</span> <span class="ss">:worker_pool</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="ss">worker_module</span><span class="p">:</span> <span class="no">PoolManagement</span><span class="o">.</span><span class="n">Worker</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">size</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">max_overflow</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="o">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If you see time out errors, change this parameter. MangaReader still supports 50 or more for concurrency.</p>
<p>And now you know how to add support for more manga sources. Feel free to send me a Pull Request! :-)</p>
]]></content:encoded><category>elixir-and-erlang</category><category>anime-and-manga</category><category>automation</category></item><item><title>The Obligatory "Why Elixir?" Personal Take</title><link>https://akitaonrails.github.io/en/2015/12/01/the-obligatory-why-elixir-personal-take/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/01/the-obligatory-why-elixir-personal-take/</guid><pubDate>Tue, 01 Dec 2015 13:26:00 -0200</pubDate><description>&lt;p&gt;So, I&amp;rsquo;ve been studying and exercising quite a bit with Elixir. José Valim recently announced the new features of the upcoming &lt;a href="https://twitter.com/josevalim/status/670595716776116224"target="_blank" rel="noopener"&gt;Elixir 1.2&lt;/a&gt;. The language design is already elegant, lean, and it keeps gradually polishing itself.&lt;/p&gt;
&lt;p&gt;First and foremost, I am a Web Applications Developer. I deal with Ruby on Rails applications and infrastructure. So, I am not a Desktop developer, a Mobile developer, Games developer or a Tools developer. This is very important to get out of the way right from the start.&lt;/p&gt;</description><content:encoded><![CDATA[<p>So, I&rsquo;ve been studying and exercising quite a bit with Elixir. José Valim recently announced the new features of the upcoming <a href="https://twitter.com/josevalim/status/670595716776116224"target="_blank" rel="noopener">Elixir 1.2</a>. The language design is already elegant, lean, and it keeps gradually polishing itself.</p>
<p>First and foremost, I am a Web Applications Developer. I deal with Ruby on Rails applications and infrastructure. So, I am not a Desktop developer, a Mobile developer, Games developer or a Tools developer. This is very important to get out of the way right from the start.</p>
<p>And by the way, as I will focus my programming efforts more and more into Elixir, it does not mean I am &ldquo;switching&rdquo; from Ruby to Elixir. I don&rsquo;t need to make exclusive choices, and in my mind, at least for a period of time, Rails joining forces with Phoenix will be a very difficult combo to beat for my web development strategies.</p>
<p>I know, Phoenix is built like Rails so why not just switch altogether: because most content-based sites don&rsquo;t need the concurrency aspects of Phoenix, and I said &ldquo;for a period of time&rdquo; because Rails still has a humongous ecosystem with more mature gems that make development easier and faster. This can change in the future, but for now the combo makes sense as I can build a normal website in Rails as I would normally do (with Devise, ActiveAdmin, Spree or whatever) and add Phoenix for stuff like WebSockets (real-time notifications, real-time chatting, background jobs that can be run more efficiently than Sidekiq, etc).</p>
<p>This article will summarize my personal take on 2 fronts:</p>
<ul>
<li><a href="#developer-roles">Other Developer Roles than just the Web</a></li>
<li><a href="#functional-concepts">&ldquo;Functional&rdquo; Concepts that Really Matter</a>
<ul>
<li><a href="#immutability">Immutability and Opaque Message Passing are VERY important</a></li>
<li><a href="#coroutines">Coroutines and Schedulers</a></li>
<li><a href="#static-dynamic">Static vs Dynamic Typing is still controversial</a></li>
<li><a href="#fault-tolerance">Fault Tolerance: Don&rsquo;t fear your code</a></li>
</ul>
</li>
<li><a href="#summary">Summary</a></li>
</ul>
<p>As anything that I want to argue about, this is <strong>lengthy</strong>, but it wouldn&rsquo;t be nice to just state something without further elaboration. In this quest for understanding, I may have confused a bit or two, so let me know in the comments section below if there are things to fix or to explain in more detail.</p>
<p>Let&rsquo;s get started.</p>
<p><a name="developer-roles"></a></p>
<h3>Other Developer Roles than just the Web<span class="hx:absolute hx:-mt-20" id="other-developer-roles-than-just-the-web"></span>
    <a href="#other-developer-roles-than-just-the-web" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In a Desktop environment, you will definitely want to make a combination of <a href="https://github.com/nwjs/nw.js/"target="_blank" rel="noopener">Node-Webkit</a> with native libraries. If you&rsquo;re in specific corporate environments, you won&rsquo;t have any choices other than plain WFC based .NET development or Java Swing. Your options have been set for quite some time, and even Visual Basic.NET still has its place. Specific toolchains will be dictated by Microsoft and Oracle/Java Community Process.</p>
<p>In Linux environments you will still use wrappers around GTK+, Qt or similar toolkits. There aren&rsquo;t a lot of ways around this.</p>
<p>If you&rsquo;re a Mobile-first developer, you <strong>do</strong> need to learn your way into Swift and Objective-C (to some extent) for iOS and the specific Java flavor for Android&rsquo;s Dalvik/ART. But I will argue that you have a lot to gain in native development if you use <a href="http://www.rubymotion.com/"target="_blank" rel="noopener"><strong>RubyMotion</strong></a>. Or, you can simply keep following Facebook&rsquo;s <a href="https://facebook.github.io/react-native/"target="_blank" rel="noopener">React Native</a> endeavor. There&rsquo;s a lot of fragmentation in this environment, you can do as little as possible with Web Mobile and Phonegap/Cordova, and build compelling apps with tools such as Ionic Framework. The only consensus is that if you really want to build the advanced next gen stuff, you want to dive deep into the native frameworks each platform has to offer.</p>
<p>If you&rsquo;re a Game Developer you want to be as close to the metal as possible. It&rsquo;s definitely feasible to write perfectly playable mobile games using any number of Javascript libraries together with HTML 5&rsquo;s technologies such as the Canvas and WebGL. But for the next gen blockbuster you will either use mature engines such as Unity or Unreal or even build your own if you&rsquo;re really invested in this field. This will require you to actually know your way into C/C++ programming. Really depends on how deep you want to go down the rabbit hole.</p>
<p>If you&rsquo;re a Tools Developer you will have the benefits from both old and new generation of languages. If you&rsquo;re closer to the Linux Kernel you will really need C/C++ in your baggage. But for the new generation of light containers (LXC), Docker, you can benefit from <strong>Go</strong>, an application development environment suited to make life easier than having to handle C/C++ idiosyncrasies. <strong>Rust</strong> is another great new choice to make it easier to - among other things - write memory-leak free small libraries and tools (I am mentioning that in particular because it&rsquo;s important for languages like Ruby or Python to be able to add performance by binding to C-based native libraries and Rust makes this task easier).</p>
<p>Different languages have different teams and different long term goals, which is why it&rsquo;s not apples and oranges to compare languages. Go, for instance, is heavier than Rust, but both are good for command-line tools, specialized daemons, and in the case of Go, networking-heavy and concurrency-heavy endeavors.</p>
<p>In my eyes, Go is a &ldquo;better&rdquo; Java or C++. And don&rsquo;t interpret this wrong: Java is still a very fine language and platform. There is hardly anything that comes close to the maturity of the JVM and the extensive ecosystem behind it. I would not think for a second of trying to rewrite complex systems written in Java such as the Lucene library or Elasticsearch/SOLR solutions, for example.</p>
<p>But the power of Standard Java is difficult to unleash without some warming up for the HotSpot to pick up steam. It makes it not a great solution for command-line tools. But now you have a good middle ground with Go. You also don&rsquo;t have a good time embedding Java into other platforms, and then you would need to go back to C, but now you have another good middle ground with Rust.</p>
<p>If you want to unleash different programming models, specially those more suited to concurrent abstractions such as the <a href="https://en.wikipedia.org/wiki/Actor_model_and_process_calculi_history"target="_blank" rel="noopener">Hoare&rsquo;s CSP</a> like Actor model, you can try <strong>Scala</strong> with <a href="http://akka.io/"target="_blank" rel="noopener">Akka</a> (which is now the standard actor library) and <strong>Clojure</strong>&rsquo;s <a href="http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/"target="_blank" rel="noopener">Pulsar/Quasar</a>. Akka and Quasar are the ones that come &ldquo;close&rdquo; (but can never match) Erlang&rsquo;s built-in OTP platform.</p>
<p>For the Web at large, you can do just fine with the current (ever changing, unstable) Node.js ecosystem, Python (Django, Plone), Ruby (Ruby on Rails, Sinatra), PHP (Zend, Laravel), even Perl has its place. Combined with mature services in different platforms (Elasticsearch in Java, PostgreSQL in C, RabbitMQ in Erlang), any big and complex Web Application can be written with any number of best-of-breed tools and combinations that best suit your needs.</p>
<p>This is an unfair, short overview, of course. I didn&rsquo;t cover every aspect of computer science or industry. There are several other active and useful languages such as Lua, Haskell, Fortran, Ada, Julia, R. The message being: you don&rsquo;t need to choose a single language, it will really depend on what you&rsquo;re going to deliver. And a true craftsman will master many tools to get the job done in the best way possible.</p>
<p><a name="functional-concepts"></a></p>
<h3>&ldquo;Functional&rdquo; Concepts that Really Matter<span class="hx:absolute hx:-mt-20" id="functional-concepts-that-really-matter"></span>
    <a href="#functional-concepts-that-really-matter" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><a href="http://www.akitaonrails.com/2015/10/28/personal-thoughts-on-the-current-functional-programming-bandwagon"target="_blank" rel="noopener">I wrote about my opinions</a> on the current surge in the so called Functional style of programming. I recommend you read it before continuing.</p>
<p>There are some aspects that are adamant if you really want to go beyond the academic research and into the real world of productivity.</p>
<p><a name="immutability"></a></p>
<h4>Immutability and Opaque Message Passing are VERY important<span class="hx:absolute hx:-mt-20" id="immutability-and-opaque-message-passing-are-very-important"></span>
    <a href="#immutability-and-opaque-message-passing-are-very-important" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>In order for computation to be fast, we are used to share data between routines. We move pointers around and change data in place.</p>
<p>It&rsquo;s not particularly fast to make things immutable and not shared. The more you make data mutable and the more you share, the harder it is to make your code run concurrently.</p>
<p>It is an important <strong>trade-off</strong>: if you see mutable data and shared state, you&rsquo;re optimizing for performance.</p>
<p>One can&rsquo;t say that a language is essentially good or bad for having mutable or immutable data. But for what it&rsquo;s worth, my personal opinion is that it&rsquo;s harder to convince users to follow conventions like <em>&ldquo;share a little as possible, mutate as little as possible.&rdquo;</em> Most will not even know about it if it&rsquo;s not built-in and enforced. I prefer having immutability enforced by default.</p>
<p>In Erlang, data is <strong>immutable</strong>. Similar to Java it <a href="http://erlang.org/pipermail/erlang-questions/2013-March/072760.html"target="_blank" rel="noopener">passes values by reference</a> in routine calls (it is not copying values between calls, as many misunderstand it).</p>
<p>And in the case of recursion it optimizes through <a href="http://learnyousomeerlang.com/recursion"target="_blank" rel="noopener">Tail Call Optimization</a> to make it faster. By the way, this is one optimization that the Java VM just can&rsquo;t quite do yet. Clojure needs special &lsquo;recur&rsquo; and &rsquo;trampoline&rsquo; calls, for example. Scala can rewrite the tail recursion to a loop in compile time, with the &lsquo;@tailrec&rsquo; annotation. <a href="http://ferd.ca/erlang-s-tail-recursion-is-not-a-silver-bullet.html"target="_blank" rel="noopener">Erlang has its own traps</a> as well, so not so black and white at the moment.</p>
<p>In Erlang, as I explained before, you run functions within completely isolated processes. If the function recurs or blocks, it stays isolated. Processes can only communicate by sending (immutable and opaque) messages to each other. Messages get queued in a &ldquo;run queue&rdquo; or &ldquo;mailbox&rdquo; and the function can choose to receive and respond to those messages. That&rsquo;s it.</p>
<p>So, you can pass values by reference between routines, or you can share data in a third party process as the mediator of data. One such infrastructure built-in to Erlang is the <strong>ETS</strong>, the <a href="http://elixir-lang.org/getting-started/mix-otp/ets.html"target="_blank" rel="noopener">Erlang Term Storage</a>, which is part of the so called OTP platform. Think of ETS as a very simple and very fast built-in key value storage like Memcached. You use it for the same use cases as a cache and it&rsquo;s as simple as just doing this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">table</span> <span class="o">=</span> <span class="ss">:ets</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:my_fancy_cache</span><span class="p">,</span> <span class="o">[</span><span class="ss">:set</span><span class="p">,</span> <span class="ss">:protected</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="ss">:ets</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="n">table</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;some_key&#34;</span><span class="p">,</span> <span class="n">some_value</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="ss">:ets</span><span class="o">.</span><span class="n">lookup</span><span class="p">(</span><span class="n">table</span><span class="p">,</span> <span class="s2">&#34;some_key&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Many might argue that Erlang&rsquo;s <strong>rigid process isolation</strong> and communication strictly restricted to opaque message passing are overkill and that you can pass by using something akin to Clojure&rsquo;s <a href="http://clojure.org/refs"target="_blank" rel="noopener">MVCC Software Transaction Memory</a>, or STM. You do have STM in Erlang, with the other built-in OTP tool, built on top of the ETS, called <strong>Mnesia</strong>. It offers the equivalent of ACID database transactions in-memory. It&rsquo;s not a new concept, but STM is not available as a language feature and it&rsquo;s still uncertain if it really is a good choice to have it.</p>
<p>An inspired result, I believe, from Clojure&rsquo;s choice of having transactional memory with history queue and snapshot isolation is shown in its crown jewel, <a href="http://www.datomic.com/"target="_blank" rel="noopener">Datomic</a>. The idea is not revolutionary by any stretch of the imagination as you have many other <a href="http://www.xaprb.com/blog/2013/12/28/immutability-mvcc-and-garbage-collection/"target="_blank" rel="noopener">prior art</a> such as RethinkDB, CouchDB, and extensions for existing databases. Good for a service, still I don&rsquo;t think it&rsquo;s a good thing to share state, even if you have a transactor around that state. Erlang&rsquo;s immutability with rigid process isolation still has no match.</p>
<p><a name="coroutines"></a></p>
<h4>Coroutines and Schedulers<span class="hx:absolute hx:-mt-20" id="coroutines-and-schedulers"></span>
    <a href="#coroutines-and-schedulers" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>You already know sub-routines, you do it all the time by partitioning large portions of code into smaller functions or methods that call each other. You may already know about a specialized kind of Coroutines in the form of <a href="http://bit.ly/1lVuLFJ"target="_blank" rel="noopener">Fibers</a> (as first implemented in Windows circa 1997).</p>
<p>Fibers offer a way for your current function execution to &ldquo;yield&rdquo; back to its caller, preserving its current state, and then the caller can &ldquo;resume&rdquo; the suspended Fiber to continue its execution from where it last yielded. This allows for non-preempted, cooperative multitasking. We have Fibers in Python, Ruby, and other languages and it allows the creation of constructions like Generators. Even Javascript can have some form of Fibers if you add libraries like <a href="https://github.com/laverdet/node-fibers"target="_blank" rel="noopener">node-fibers</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">Fiber</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;fibers&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">sleep</span><span class="p">(</span><span class="nx">ms</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">fiber</span> <span class="o">=</span> <span class="nx">Fiber</span><span class="p">.</span><span class="nx">current</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">fiber</span><span class="p">.</span><span class="nx">run</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span> <span class="nx">ms</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Fiber</span><span class="p">.</span><span class="k">yield</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The call to &lsquo;yield&rsquo; suspends the current execution until the function in the &lsquo;setTimeout&rsquo; is called. Then it calls &lsquo;run&rsquo;, which resumes the previously yielded function. This is still &ldquo;rudimentary&rdquo; compared to coroutines: because the function itself has to yield control out to the reactor event loop, in the case of a Node.js application. If you don&rsquo;t, you will block the event loop in a single threaded Node.js process, and therefore you block everything until the function finishes, defeating the whole purpose. And this is one of those &ldquo;conventions&rdquo; that &ldquo;good&rdquo; programmers should follow, but most will forget.</p>
<p>Fibers are useful to make it less ugly to program in a rudimentary Reactor environment, where you depend on callbacks calling callbacks and you end up with the anti-pattern of <a href="https://bjouhier.wordpress.com/2012/03/11/fibers-and-threads-in-node-js-what-for/"target="_blank" rel="noopener">callback pyramid of doom</a>. With Fibers you can program as you would in a synchronous imperative language transforming this ugly Javascript code:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">archiveOrders</span><span class="p">(</span><span class="nx">date</span><span class="p">,</span> <span class="nx">cb</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">db</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">conn</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">return</span> <span class="nx">cb</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">conn</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="s2">&#34;selectom orders where date &lt; ?&#34;</span><span class="p">,</span>  
</span></span><span class="line"><span class="cl">               <span class="p">[</span><span class="nx">date</span><span class="p">],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">orders</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">return</span> <span class="nx">cb</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">helper</span><span class="p">.</span><span class="nx">each</span><span class="p">(</span><span class="nx">orders</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">order</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">conn</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="s2">&#34;insert into archivedOrders ...&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                     <span class="p">[</span><span class="nx">order</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span> <span class="p">...],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">return</span> <span class="nx">cb</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="nx">conn</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="s2">&#34;delete from orders where id=?&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                       <span class="p">[</span><span class="nx">order</span><span class="p">.</span><span class="nx">id</span><span class="p">],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">return</span> <span class="nx">cb</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nx">next</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">          <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;orders been archived&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">cb</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Into this more manageable thing:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">archiveOrders</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">date</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">conn</span> <span class="o">=</span> <span class="nx">db</span><span class="p">.</span><span class="nx">connect</span><span class="p">().</span><span class="nx">wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="nx">conn</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="s2">&#34;selectom orders where date &lt; ?&#34;</span><span class="p">,</span>  
</span></span><span class="line"><span class="cl">             <span class="p">[</span><span class="nx">date</span><span class="p">]).</span><span class="nx">wait</span><span class="p">().</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">order</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">conn</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="s2">&#34;insert into archivedOrders ...&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                 <span class="p">[</span><span class="nx">order</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span> <span class="p">...]).</span><span class="nx">wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">conn</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="s2">&#34;delete from orders where id=?&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                 <span class="p">[</span><span class="nx">order</span><span class="p">.</span><span class="nx">id</span><span class="p">]).</span><span class="nx">wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;orders been archived&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}).</span><span class="nx">future</span><span class="p">();</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The entire <a href="https://en.wikipedia.org/wiki/Futures_and_promises"target="_blank" rel="noopener">Promises, Futures</a> debacle depends in part on proper Fibers. Javascript, being a very poor design, does not come with anything built-in and hence the proliferation of Fibers, Deferreds, Promises, Futures implementations that can never reach neither consensus nor people actually using them at large.</p>
<p>So, Fibers are ok. Coroutines are better because you have multiple points of suspending a function and more. And even better, in Erlang one doesn&rsquo;t need to even think about rudimentary Reactor loops (yes, reactors are a rudimentary construction for concurrency when you have no other good choice): it has <strong>transparent asynchronous calls</strong>. Everything in Erlang is asynchronous and non-blocking but you don&rsquo;t deal with callback pyramids because there is something better underneath: the <strong>Scheduler</strong>.</p>
<p>By the way, I find it very frustrating that <a href="http://stackoverflow.com/questions/18058164/is-golang-goroutine-a-coroutine"target="_blank" rel="noopener">Go&rsquo;s &ldquo;goroutines&rdquo; <strong>are not</strong> proper &ldquo;coroutines&rdquo;</a>.</p>
<p>For each Erlang process with SMP (symmetric multiprocessing) support there will be <strong>one</strong> real thread per CPU core available in your system, and for each thread there will be one single Scheduler to manage the internal green threads (processes) and run-queue.</p>
<p>As a programmer, I don&rsquo;t have to &ldquo;remember&rdquo; to yield control back to a passive event loop. The scheduler will take care of balancing computation time to each concurrent process. If a process is taking too long the Scheduler can choose to suspend it and give time to other routines. Erlang defines a <a href="http://erlang.org/pipermail/erlang-questions/2001-April/003132.html"target="_blank" rel="noopener">&ldquo;reduction&rdquo;</a> and that there are different priority levels of functions. If a function takes more than 2,000 reductions, the Scheduler can choose to suspend it. If you have 8 CPU cores, but the computation on the processes are not heavy, the VM can choose to just use 1 or 2 Schedulers and leave the other 6 idle so the hardware can turn the cores off to save energy (!!). Yep, Erlang is even Eco Friendly!</p>
<p>And we need to repeat this again: because each process is <strong>rigidly isolated</strong>, with immutable data and no shared state, it&rsquo;s easier to suspend a running process. In the case of the JVM this is usually implemented by raising checked exceptions and have everybody implement some Suspendable interface. It can be done using this <a href="http://www.matthiasmann.de/content/view/24/26/"target="_blank" rel="noopener">3rd party Java continuation library</a> where you yield by raising an Exception (!) Nasty stuff.</p>
<p>Rust is <a href="https://github.com/rustcc/coroutine-rs"target="_blank" rel="noopener">still implementing</a> something for coroutines as well, but still nothing as mature. But again, coroutines is just part of the story, you would need a heavier system with userland schedulers for it to make sense. Go is a better candidate to incorporate such a system in its runtime, but it also comes up short on being able to implement all this. There is <a href="http://www.jerf.org/iri/post/2930"target="_blank" rel="noopener">Suture</a>, an attempt to have some of OTP in Go, but it can&rsquo;t be done. Even Akka, the first mainstream OTP clone for Scala, can&rsquo;t come close because of the JVM shortcomings. <a href="http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/"target="_blank" rel="noopener">Clojure with Pulsar/Quasar</a>, comes <strong>closer</strong>, but is not there yet.</p>
<p>Now, the Erlang Scheduler is not only capable of suspending and resuming processes but it also takes care of message passing between them. So each Scheduler has its own run-queue to queue and dispatch messages. Again, because data is immutable, you only need some form of locking when you want another Scheduler (in another real thread) to take over a few processes in order to balance processing between cores. Erlang has supported SMP <a href="http://erlang.org/pipermail/erlang-questions/2008-September/038231.html"target="_blank" rel="noopener">since OTP R12B</a> (we are at R18 right now, and still evolving).</p>
<p>Most languages still rely on the OS real threads preemptive model to do multitasking. And this is heavy and slow because of all the context switching involved and all the locking logic most programmers will do wrong (the best practice for concurrency is: do not use threads, chances are you will screw up). Again, we make the right assumptions first: programmers can&rsquo;t do proper multithreading, so let a Scheduler do it, when necessary, and avoiding slow OS context switching as much as possible. Suspendable green threads combined with a userland Scheduler coordinating cooperative switching is a way faster and safer choice.</p>
<p>If you want to learn more about coroutines this <a href="http://www.inf.puc-rio.br/~roberto/docs/corosblp.pdf"target="_blank" rel="noopener">Lua Paper</a> on the subject explains in more details what I just elaborated:</p>
<blockquote>
Implementing a multitasking application with Lua coroutines is straightforward. Concurrent tasks can be modeled by Lua coroutines. When a new task is created, it is inserted in a list of live tasks. A simple task dispatcher can be implemented by a loop that continuously iterates on this list, resuming the live tasks and removing the ones that have finished their work (this condition can be signalled by a predefined value returned by the coroutine main function to the dispatcher). Occasional fairness problems, which are easy to identify, can be solved by adding suspension requests in time-consuming tasks.
</blockquote>
<p><a name="static-dynamic"></a></p>
<h4>Static vs Dynamic Typing is still controversial<span class="hx:absolute hx:-mt-20" id="static-vs-dynamic-typing-is-still-controversial"></span>
    <a href="#static-vs-dynamic-typing-is-still-controversial" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>We saw a heavier movement from bureaucratic static typed systems (mainly Java prior to 6, C# prior to 4, C++) to purely dynamic languages such as Perl in the late 80&rsquo;s, Python in the late 90&rsquo;s, Ruby in mid 2000&rsquo;s. We tried to go from <em>&ldquo;making the compiler happy&rdquo;</em> to <em>&ldquo;making programmers happy&rdquo;</em>, which makes more sense if you ask me.</p>
<p>Scala, Groovy, Haskell, Swift brought a very practical middle ground with <a href="https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system"target="_blank" rel="noopener">Hindley-Milner</a> derived <strong>Type Inference</strong> systems, in a way that we can code kinda like in dynamic languages but with the compiler doing more work to infer types for us before generating the final executable bytecode.</p>
<p>But there is a <strong>big</strong> catch: it&rsquo;s very difficult to <strong>hot swap</strong> code inside the runtime if you have static signatures. I&rsquo;m not saying that it is impossible, but a lot more difficult. You can reload code in Java (one example being <a href="https://github.com/spring-projects/spring-loaded"target="_blank" rel="noopener">Spring Loaded</a>) or Haskell (there is a hotswap plugin and other alternatives). You don&rsquo;t do granular reloads in a statically typed language because if you want to change the signature of a method, you have to change the graph that depends on that signature. It&rsquo;s doable, albeit, cumbersome.</p>
<p>In Erlang, because there is no such hard dependencies, and again because of inherent advantages of it only having immutable data with no shared state, and dependencies limited to opaque message passing, you can granularly reload one single module and most important of all: you can implement simple callbacks to <strong>transform the old state of a process into a new state structure</strong>, because just reloading the code is half the story if you will have hundreds of old processes restarting into the new code but having to deal with previous state. In an Erlang GenServer, you just implement this single callback:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">code_change</span><span class="p">(</span><span class="no">OldVersion</span><span class="p">,</span> <span class="no">CurrentState</span><span class="p">,</span> <span class="n">_Extra</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="no">NewState</span><span class="p">}</span><span class="o">.</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So, while Type Inference is a nice middle ground, the flexibility of Dynamic Typing goes beyond being easy for programmers to use. As with Python, Ruby, Javascript, Perl and other dynamic typed languages, you will want to cover your code with proper test suites - which should not be optional in strong typed languages anyway. There is no doubt a compiler&rsquo;s static analysis helps a lot, but it&rsquo;s my personal take that dynamic typing allows me more flexibility.</p>
<p><a name="fault-tolerance"></a></p>
<h3>Fault Tolerance: Don&rsquo;t fear your code<span class="hx:absolute hx:-mt-20" id="fault-tolerance-dont-fear-your-code"></span>
    <a href="#fault-tolerance-dont-fear-your-code" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>With Dynamic Typing we will end up again in the endless discussion of <em>&ldquo;programmers never do it right, we need a compiler to enforce static rules&rdquo;</em>. And you are almost right: programmers screw up, but a compiler will not save your day anyway, and worst: it might give you a false sense of security. There is no worse security hole than false sense of security. A test suite is way better to assert proper implementation, and it&rsquo;s still no hard guarantee.</p>
<p>I said that this is controversial, as a more rigid static typing system is more productive than having to unit test every input and output types of every function. But if you thought that, you&rsquo;re at least wrong in that this is not what unit tests are for: it&rsquo;s for testing unit behaviors, in spite of what the types are. Testing types is what we call &ldquo;chicken typing&rdquo; and it&rsquo;s another form of defensive programming. You must test for behavior, not for stuff that a compiler would check. And again, having the static typing hinges my flexibility because now I have to constantly fight the typing, add more boilerplate, and ultimately if the behavior is wrong, the code is wrong, despite the type checks.</p>
<p>For a system to be <strong>&ldquo;tolerant to buggy code or uncaught failures&rdquo;</strong> is the opposite of littering your code with guarding statements like &ldquo;try/catch&rdquo; or &ldquo;if err == x&rdquo; or having some form static level checking with Result monads (that most people will just unwrap anyway). Guards can only go so far. And yes, this is anecdotal as there are no statistics that &ldquo;everybody will unwrap&rdquo; and most good programmers won&rsquo;t, but if experience tells me one thing, it&rsquo;s that bad programmers will fall for &ldquo;try/catch&rdquo; the whole thing when they don&rsquo;t know what to do anyway.</p>
<p>You need a system that allows any buggy code to fail and not crash the environment around it, which would bring it into an inconsistent, corrupt, state.</p>
<p>The problem with faulty code is that it leaves the state in a position where it has nowhere else to go. And if this state is shared, you leave every other bit of code in a position where they can&rsquo;t decide where to go next. You will need to shut everything down and restart from the last known good state. Almost all &ldquo;continuous delivery&rdquo; workflows are implemented around restarting everything.</p>
<p>Instead of having to bring everything down in case you forget to try/catch something, you can rely on Erlang&rsquo;s underpinnings of not sharing state, having immutable data, and - most importantly - having the rigidly isolated lightweight process system to make the process that is holding the faulty code to <strong>shut down and warn its Supervisor</strong>. This is the general idea behind the so called OTP in Erlang.</p>
<p>The idea of a Supervisor is to have a small process that monitors other processes. The Supervisor has as little code as possible in order to rarely (or never) fail (the best kind of code is &ldquo;no code&rdquo;). Once a faulty process crashes because of uncaught exception or other undeterministic reasons, it sends a notification message to the Supervisor run-queue mailbox, and then die cleanly. The Supervisor then chooses what to do based on its underlying restart strategy.</p>
<p>Let&rsquo;s say you have a list of URLs you&rsquo;re scraping. But you didn&rsquo;t anticipate dirty structures in your parsing logic. The process doing the scraping crashes and dies. The Supervisor is notified and chooses to restart the process, giving the new process the previous state - the URL list - and now that the faulty URL is not there anymore, the new process can happily continue the job with the next URL in the list.</p>
<p>This is a simple example of the dynamic between the Erlang VM, the Supervisor and its children workers. You can go further and have several Applications, which in turn start up new Supervisors, which in turn start up children processes. And you have a Supervisor Tree that can trap exits and restart granular bits of your runtime code without bringing the rest of the system into an inconsistent state.</p>
<p>Such is the beauty of the rigid process isolation concept.</p>
<p>Every I/O in the system is wrapped in <a href="http://www.erlang.org/doc/tutorial/c_port.html"target="_blank" rel="noopener">Ports</a>, which obey Async/callback logic transparently without you having to create pyramids of callbacks. The process consuming such ports just gets suspended by the Scheduler until the Async call returns and it can resume work. No callback pyramid hell. No need for rudimentary Fiber implementations to allow for rudimentary Promises/Futures systems. Just coroutines inside processes that can be suspended and resumed by the Scheduler. Fewer opportunities for programmer errors to stack up.</p>
<p>So, a programmer will forget to code every possible branch of execution and because he knows that, the <strong>very worst thing</strong> that can happen is not if he forgets an uncaught exception, but if he decides to program <strong>defensively</strong> and add general conditions to trap <strong>any</strong> error and never raise it. You&rsquo;ve seen it before, when you find code that is trapped inside generic try/catch blocks, trying to avoid every error possible. But what really happens is that the system may not crash but your logic and your processing is faulty at its core. And you won&rsquo;t find out about it, because it doesn&rsquo;t crash, therefore no one is ever notified! You will not end up with fewer bugs, you will end up with a mountain of logic bugs that are never noticed because they are all swallowed!!</p>
<p>This is the core of Fault Tolerance: do not fear Erlang, fear the programmers! Instead, we should do what Joe Armstrong presented in his seminal paper <a href="http://www.erlang.org/download/armstrong_thesis_2003.pdf"target="_blank" rel="noopener">&ldquo;Making reliable distributed systems in the presence of software errors&rdquo;</a>. This is both a detailed guide into Erlang and OTP but also his arguments on how to write fault tolerant systems by making the programmer write code as clearly as he originally intended, without the need to be defensive, with the confidence that if he misses something, OTP will be there to catch it and not let the system die, but instead to give a chance to fix it and reload it without disrupting other good parts of the system.</p>
<p>This is the ultimate goal of good programming: not being defensive, not putting try/catch everywhere because you&rsquo;re afraid.</p>
<p><a name="summary"></a></p>
<h3>Summary<span class="hx:absolute hx:-mt-20" id="summary"></span>
    <a href="#summary" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>This short summary is the reason why Erlang sounds very compelling for my Web Development needs, or for any scalable complex system, at least for me.</p>
<p>Every other language had to make trade-offs. Every new language on top of the JVM has to deal with limitations inherent to how the JVM was originally architected. Rust still can build better abstractions, but its scope is for smaller tools and libraries, not distributed complex systems. Yes, eventually it can do whatever C/C++ can do, and Mozilla is actually basing the core of its next generation browser core on Rust. This will bootstrap better higher level libraries and frameworks, the same way Apple using Objective-C created the entire set of Core frameworks that make implementing complex applications much easier.</p>
<p>Go made a choice for keeping familiarity with its C++ inheritance. Of course, it has tons of useful features, in particular the built-in goroutines that make concurrent coding much easier than in previous languages.</p>
<p>Haskell is too strict for most programmers (yes, Monads, still difficult for the average programmer to fully grasp) and despite contrary opinions, to me it still feels like it appeals more to researchers than everyday developers. Other dynamic languages such as Ruby, Python, may still go the way of Erlang with Ruby adding some immutability (<a href="https://bugs.ruby-lang.org/issues/11473"target="_blank" rel="noopener">in 2.3</a> with immutable String by default), but it&rsquo;s still a long way to go.</p>
<p>Erlang has everything, as Joe Armstrong envisioned a fault tolerant system to be. It started as an exercise implemented in Prolog in 1986. It migrated from the previous JAM compiler to the current BEAM VM in 1998. It added SMP around 2008. It has been gradually evolving, polishing its rough edges, being really battle tested in really mission critical systems for decades. It&rsquo;s ready for us, right now.</p>
<p>It has Fault Tolerance guaranteed by the principles of immutable data, no shared state, pure opaque message passing, and suspendable processes, all managed by Schedulers. This guarantees that faulty routines can crash one single process but not the entire system and definitely not bringing state of other process to inconsistent, corrupted, states.</p>
<p>Then you can instrument your virtual machine and check that Supervisors are restarting children more than you want and decide to fix the buggy code. And once you do, you can choose <strong>not</strong> to shut down and restart the entire system to reload the code fixes, you can do it granularly, on the fly, with running processes that will pick up the fixes once the Supervisor restarts it. The granular hot swap of code is guaranteed because there is no hierarchy of types to care about.</p>
<p>And because you have proper coroutines with no hierarchy of shared state dependencies, you can have <strong>asynchronous exceptions</strong> that can forcefully shut down processes without creating side effects to other running processes. A Supervisor may choose to restart its entire children list when one of its children crashes and another of its children depended on that previous process. You can switch from &ldquo;one for one&rdquo; restart strategy to &ldquo;one for all&rdquo; (like the Musketeers).</p>
<p>There is only one problem with Erlang: it was not designed for &ldquo;programmer happiness&rdquo;, a concept we&rsquo;ve come to take for granted because of Ruby and newer languages.</p>
<p>Erlang has its roots in Prolog and it shows. Once you step up and really dive deep exercising with the language you can possibly get used to it. But if you came from more modern dynamic languages such as Ruby, Python, Groovy, you will definitely miss the comfortable modern constructions.</p>
<p>Elixir is the missing piece, the Philosopher&rsquo;s Stone if you will, that can unlock all the 30 years of refinements, maturity, industry battle tested technologies in large scales, to the average programmer.</p>
<p>It brings many modern constructs such as making macros easier in order to allow for Domain Specific Languages, having testable comments in your code, adding a more modern standard library that is easily recognizable from a Ruby or Clojure point of view, polymorphism through Protocols, and so on.</p>
<p>This is one example of Elixir straight from its source code tests:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Code</span><span class="o">.</span><span class="n">require_file</span> <span class="s2">&#34;../test_helper.exs&#34;</span><span class="p">,</span> <span class="n">__DIR__</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Mix</span><span class="o">.</span><span class="n">ArchiveTest</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">MixTest</span><span class="o">.</span><span class="n">Case</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">doctest</span> <span class="no">Mix</span><span class="o">.</span><span class="n">Archive</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;archive&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">in_fixture</span> <span class="s2">&#34;archive&#34;</span><span class="p">,</span> <span class="n">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">File</span><span class="o">.</span><span class="n">write</span> <span class="s2">&#34;.elixir&#34;</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.0.0&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">Mix</span><span class="o">.</span><span class="n">Archive</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">,</span> <span class="s2">&#34;sample.ez&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">archive</span> <span class="o">=</span> <span class="s1">&#39;sample.ez&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="no">File</span><span class="o">.</span><span class="n">exists?</span><span class="p">(</span><span class="n">archive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="s1">&#39;sample/.elixir&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="s1">&#39;sample/priv/not_really_an.so&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="s1">&#39;sample/ebin/Elixir.Mix.Tasks.Local.Sample.beam&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="s1">&#39;sample/ebin/local_sample.app&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="nb">name</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">files</span><span class="p">}</span> <span class="o">=</span> <span class="ss">:zip</span><span class="o">.</span><span class="n">list_dir</span><span class="p">(</span><span class="n">archive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Enum</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">match?</span><span class="p">({</span><span class="ss">:zip_file</span><span class="p">,</span> <span class="o">^</span><span class="nb">name</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">},</span> <span class="o">&amp;</span><span class="mi">1</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Code that can be written like this automatically makes me grin.</p>
<p>This is the perfect combination. Again, it is not without shortcomings. Erlang is by no means a fast language. It is way faster than Ruby, Python, or other interpreted languages. It can be made a bit faster with native compilation through the HIPE compiler, but still nowhere near the speeds of Go, Rust, or any Java derivative such as Scala, Groovy or Clojure.</p>
<p>So, if you really need raw power of computation, you will want Go or Java. Again, I&rsquo;m not saying there are only those 2, but it&rsquo;s the usual choices if you don&rsquo;t want to go down to C/C++. Haskell has terrific performance, but its learning curve is less than stellar.</p>
<p>Erlang is a whole system, it has its own scheduling system, it controls living, breathing processes that each have its own garbage collector, it controls system signal trapping and so on. It was designed to be a full server. It is much smaller than a full blown Java Enterprise Edition container, so small that you can actually package command line tools that do start up fast enough. But this is not the sweet spot. For that purpose you will be better off with Go or even Rust.</p>
<p>For the same reasons it was not made to be an embeddable language the way Lua is. It was not made to create libraries that can be easily linked to via FFI or C-like function exports, the way Rust can.</p>
<p>There are ways to create desktop-class applications, specially with cross-platform <a href="http://www.erlang.org/doc/apps/wx/chapter.html"target="_blank" rel="noopener">wxWidgets</a>, the way the built-in Observer instrumentation application is done, but Erlang was not built to be a desktop toolkit.</p>
<p>Also because it prioritizes correctness, rigidly isolated processes communicating just by opaque messages, immutable and non-shared states, it means that Erlang is not suited to hard data science processing. So I doubt it&rsquo;s the best choice for Big Data analytics, DNA sequencing and other hard stuff that tools like Julia, R, Fortran, are better choices. It&rsquo;s not the same thing to say that it can&rsquo;t be a good database core, Riak and CouchDB proved that already. But complex queries on top of high volumes of data is not the sweet spot as well.</p>
<p>So, Erlang is good for distributed systems, with high concurrency of opaque message exchange and proxying. The exact scenario where the Web is. Web Applications heavy load of throughput that need real time chats and notifications, heavy and time consuming payment transactions, gathering of data from many sources in order to reduce them to consumable HTML or JSON responses.</p>
<p>But for the average Web developer (and by &ldquo;average&rdquo; I mean minimally able to architect the kinds of complex systems we deal with everyday in web development, not simple static website construction), Erlang used to be a real challenge, and now we can have the comfort of a real modern language with hints of Ruby and Clojure, without the complexities of strong typing but with the security of its built-in Fault Tolerance constructions in order to deliver highly reliable, highly scalable, modern Web applications.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>software-architecture</category></item><item><title>"Yocto Services"! And My First Month with Elixir!</title><link>https://akitaonrails.github.io/en/2015/11/25/yocto-services-and-my-first-month-with-elixir/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/11/25/yocto-services-and-my-first-month-with-elixir/</guid><pubDate>Wed, 25 Nov 2015 18:04:00 -0200</pubDate><description>&lt;p&gt;Wow, for the past month (almost, from Oct 27 to Nov 25) I decided that it was past time to dive deep and actually learn Elixir. I did just that, I am still a beginner but I feel very confident that I can tackle Elixir based projects now.&lt;/p&gt;
&lt;p&gt;For my learning process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I read the entire &lt;a href="http://elixir-lang.org/getting-started/introduction.html"target="_blank" rel="noopener"&gt;official Elixir Documentation&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;I read the entire Dave Thomas&amp;rsquo; &lt;a href="https://pragprog.com/book/elixir/programming-elixir"target="_blank" rel="noopener"&gt;Programming Elixir&lt;/a&gt; book;&lt;/li&gt;
&lt;li&gt;I watched almost all &lt;a href="http://elixirsips.com/"target="_blank" rel="noopener"&gt;Elixir Sips&lt;/a&gt; screencasts;&lt;/li&gt;
&lt;li&gt;I read the entire Benjamin Tan Wei Hao&amp;rsquo;s &lt;a href="https://www.manning.com/books/the-little-elixir-and-otp-guidebook"target="_blank" rel="noopener"&gt;The Little Elixir &amp;amp; OTP Guidebook&lt;/a&gt; book;&lt;/li&gt;
&lt;li&gt;I did a number of tutorials and exercises.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And finally, I documented everything I learned almost every day in the following articles:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Wow, for the past month (almost, from Oct 27 to Nov 25) I decided that it was past time to dive deep and actually learn Elixir. I did just that, I am still a beginner but I feel very confident that I can tackle Elixir based projects now.</p>
<p>For my learning process:</p>
<ol>
<li>I read the entire <a href="http://elixir-lang.org/getting-started/introduction.html"target="_blank" rel="noopener">official Elixir Documentation</a>;</li>
<li>I read the entire Dave Thomas&rsquo; <a href="https://pragprog.com/book/elixir/programming-elixir"target="_blank" rel="noopener">Programming Elixir</a> book;</li>
<li>I watched almost all <a href="http://elixirsips.com/"target="_blank" rel="noopener">Elixir Sips</a> screencasts;</li>
<li>I read the entire Benjamin Tan Wei Hao&rsquo;s <a href="https://www.manning.com/books/the-little-elixir-and-otp-guidebook"target="_blank" rel="noopener">The Little Elixir &amp; OTP Guidebook</a> book;</li>
<li>I did a number of tutorials and exercises.</li>
</ol>
<p>And finally, I documented everything I learned almost every day in the following articles:</p>
<ul>
<li><a href="http://www.akitaonrails.com/2015/10/27/how-fast-is-elixir-phoenix"target="_blank" rel="noopener">How Fast is Elixir/Phoenix?</a></li>
<li><a href="http://www.akitaonrails.com/2015/10/28/personal-thoughts-on-the-current-functional-programming-bandwagon"target="_blank" rel="noopener">Personal Thoughts on the Current Functional Programming Bandwagon</a></li>
<li><a href="http://www.akitaonrails.com/2015/10/29/phoenix-experiment-holding-2-million-websocket-clients"target="_blank" rel="noopener">Phoenix Experiment: Holding 2 Million Websocket clients!</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/03/minha-primeira-semana-aprendendo-elixir"target="_blank" rel="noopener">My first week learning Elixir</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/18/ex-manga-downloadr-um-exercicio-com-elixir"target="_blank" rel="noopener">Ex Manga Downloader, an exercise with Elixir</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener">Ex Manga Downloadr - Part 2: Poolboy to the rescue!</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/20/phoenix-15-minute-blog-comparacao-com-ruby-on-rails"target="_blank" rel="noopener">Phoenix &ldquo;15 Minute Blog&rdquo; comparison to Ruby on Rails</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/22/observando-processos-em-elixir-the-little-elixir-otp-guidebook"target="_blank" rel="noopener">Observing Processes in Elixir - The Little Elixir &amp; OTP Guidebook</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir"target="_blank" rel="noopener">ExMessenger Exercise: Understanding Nodes in Elixir</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/25/elixir-101-apresentando-a-sintaxe"target="_blank" rel="noopener">Elixir 101 - Introducing the Syntax</a></li>
</ul>
<p>Yes, I am prolific, and highly focused. I did the learning and writing all within less than a month (it would&rsquo;ve been less if I could have used night time and weekends). So I&rsquo;d say that the average developer would take at least 3 to 4 months to cover the same material.</p>
<h3>Initial Thinking: Yocto Services!<span class="hx:absolute hx:-mt-20" id="initial-thinking-yocto-services"></span>
    <a href="#initial-thinking-yocto-services" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>For good or for worse, we are in the dawn of the Micro Services architecture. In summary it&rsquo;s fragmenting your monolith into smaller applications that respond to HTTP based endpoints - that people call &ldquo;APIs&rdquo; - and that are responsible for a very narrow set of responsibilities. Then you create a &ldquo;front-end&rdquo; web application that will consume those services, such as analytics, payment methods, user authentication directories, and so on.</p>
<p>This is no novelty, of course, having HTTP APIs that return JSON structures is just a fancier way of doing the same, good, old <strong>Remote Procedure Calls</strong>, or RPCs, a technology we have had for decades to interconnect clients and servers in a network. But I digress.</p>
<p>If this is what people call &ldquo;Micro&rdquo; services, I think of Elixir processes as <strong>&ldquo;Yocto&rdquo;</strong> Services! (Milli &gt; Micro &gt; Nano &gt; Pico &gt; Femto &gt; Atto &gt; Zepto &gt; Yocto, by the way - I may have just invented a new term here!)</p>
<p>I described a bit of the Processes infrastructure, how to spawn them, how to exchange messages between them, and how to link them together. So, go read <a href="http://www.akitaonrails.com/en/2015/11/22/observando-processos-em-elixir-the-little-elixir-otp-guidebook"target="_blank" rel="noopener">my previous post</a> if you haven&rsquo;t already.</p>
<p>Inside an Elixir app you will find many processes, some from the VM running the show and some from your own app. If you architected it correctly, you&rsquo;ve implemented an OTP application, with proper groups of Supervisors and Children, all organized in a Supervision Tree. A small worker dies, its supervisor knows how to deal with it.</p>
<p>Now, here&rsquo;s the mental model: think of an Elixir process as a tiny micro-service - a <strong>Yocto service</strong>, if you will! - inside your application. In a Phoenix application, for example, you don&rsquo;t import a &ldquo;database library&rdquo; you actually spin up a &ldquo;Database Service&rdquo; (which is an Ecto Repo) that runs in parallel with the Endpoint application that responds to HTTP requests coming from the internet. The code in your controllers and models &ldquo;consume&rdquo; and send messages to the Ecto Repo &ldquo;Service&rdquo;. This is how you can visualize what&rsquo;s going on.</p>
<p>I have shown the Observer in the previous article as well. You will find a large tree if you open it up inside the IEx shell of a Phoenix application:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/531/big_phoenix-observer.png" alt="Observer Phoenix"  loading="lazy" /></p>
<p>In summary you will find the following section in the Pxblog Phoenix App Supervision Tree:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Pxblog.Supervisor
- Pxblog.Endpoint
    &#43; Pxblog.Endpoint.Server
    &#43; Pxblog.PubSub.Supervisor
        * Pxblog.PubSub
        * Pxblog.PubSub.Local
- Pxblog.Repo
    &#43; Pxblog.Repo.Pool</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is the Pxblog I explained in the <a href="http://www.akitaonrails.com/en/2015/11/20/phoenix-15-minute-blog-comparacao-com-ruby-on-rails"target="_blank" rel="noopener">Phoenix and Rails comparison article</a> I published a few days ago.</p>
<p>I still haven&rsquo;t read the source code of Phoenix, but if I am interpreting the Observer correctly, the Endpoint.Server controls a pool of processes that are TCP listeners that the application is ready to accept requests from, concurrently, with overflowing to accept more connections (I believe it&rsquo;s a pool implementation like Poolboy, which I explained in <a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener">Part 2 of the Ex Manga Downloader article</a>).</p>
<p>Then, you have the PubSub.Supervisor and PubSub.Local applications that I believe support the WebSocket channels.</p>
<p>The Repo alone controls 10 initial processes in its pool, possibly a database connection pool. Notice how Endpoint and Repo groups are in parallel branches in the supervision tree. If Repo fails for some external database problems, the Endpoint group does not have to fail. This is what&rsquo;s declared in the Pxblog Application definition at <tt>lib/pxblog.ex</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>defmodule Pxblog do
  use Application

  # See http://elixir-lang.org/docs/stable/elixir/Application.html
  # for more information on OTP Applications
  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      # Start the endpoint when the application starts
      supervisor(Pxblog.Endpoint, []),
      # Start the Ecto repository
      worker(Pxblog.Repo, []),
      # Here you could define other workers and supervisors as children
      # worker(Pxblog.Worker, [arg1, arg2, arg3]),
    ]

    # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Pxblog.Supervisor]
    Supervisor.start_link(children, opts)
  end
  ...
end</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>See how it defines Endpoint and Repo under the Pxblog.Supervisor.</p>
<p>I can go on and forcefully kill the entire Pxblog.Repo node from the Supervision Tree using the Observer, like I did in the previous article, and the proper strategy kicks in, the Phoenix Supervisor successfully restarts the Repo, and no one will ever notice something crashed underneath.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/532/big_phoenix_kill_repo.png" alt="Kill Repo"  loading="lazy" /></p>
<p>From the IEx I can still make more calls to the Repo like this and it responds as if it had never crashed:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(4)&gt; Pxblog.Repo.all(Pxblog.User)
[debug] SELECT u0.&#34;id&#34;, u0.&#34;username&#34;, u0.&#34;email&#34;, u0.&#34;password_digest&#34;, u0.&#34;inserted_at&#34;, u0.&#34;updated_at&#34; FROM &#34;users&#34; AS u0 [] OK query=78.2ms queue=3.2ms
[%Pxblog.User{__meta__: #Ecto.Schema.Metadata&lt;:loaded&gt;,
  email: &#34;akitaonrails@me.com&#34;, id: 1,
  inserted_at: #Ecto.DateTime&lt;2015-11-20T14:01:09Z&gt;, password: nil,
  password_confirmation: nil,
  password_digest: &#34;...&#34;,
  posts: #Ecto.Association.NotLoaded&lt;association :posts is not loaded&gt;,
  updated_at: #Ecto.DateTime&lt;2015-11-20T14:01:09Z&gt;, username: &#34;akitaonrails&#34;}]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And the way I think about this is: my IEx shell is sending a message to the Yocto Service called Pxblog.Repo (in reality it&rsquo;s forwarding messages to the database adapter that then checks out a process from the pool). Just like I would consume external Micro Services through HTTP APIs.</p>
<p>So the landscape of your application is comprised of a series of processes and groups of supervised processes all working to compose a larger structure. As I said in previous articles, should one group of process collapse, its Supervisor kicks in, traps the error and uses its strategy to, for example, restart all of its children processes, bringing the app to a consistent state once more, and without you having to restart the entire Elixir application.</p>
<p>So each process can be a full blown Yocto Service, running online and waiting for other services to consume it, such as the Repo&rsquo;s workers.</p>
<h3>Divide and Conquer<span class="hx:absolute hx:-mt-20" id="divide-and-conquer"></span>
    <a href="#divide-and-conquer" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Again, as a disclaimer, I am still new to Elixir but the way I find easier to understand it, is like this:</p>
<ul>
<li>
<p>If you must deal with external resources, be it a File, or a Network Connection, or anything outside of the Erlang VM, you will want it to be a GenServer.</p>
</li>
<li>
<p>Then, if you have a GenServer, you want to start it up under a Supervisor (usually, the simple boilerplate that defines the children and restart strategy).</p>
</li>
<li>
<p>The number of GenServer processes you want to start up depends on how many parallel processes you want to have running. For example, if it is a database service, there is no point in starting up more than the available number of maximum connections your database allows. If you have several files to process, you want at most the number of available files or - in practice - just a few processes to deal with batches of files. You will usually want a pool of GenServer processes, and in this case you want to use Poolboy.</p>
</li>
<li>
<p>One GenServer may call other GenServers. You don&rsquo;t want to use a try/catch exception handling mechanism because you just need the particular GenServer process to crash if something goes wrong: if the file is corrupted or doesn&rsquo;t exist or if the network goes unstable or disconnects. The Supervisor will replace that process with a new GenServer process in its place and refill the Pool if needed.</p>
</li>
<li>
<p>You can make GenServers <a href="http://www.akitaonrails.com/en/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir"target="_blank" rel="noopener">talk remotely</a> using the Node feature I explained 2 posts ago, with the ExMessenger example. Then it would be like a normal Micro Services architecture, but where the inside Yocto Services are actually doing the talking.</p>
</li>
<li>
<p>Any transformation with no side-effects (transforming a simple input into a simple output), like getting an HTML body string and parsing it into a List of tuples, can be organized in a normal Module. Refer to libraries like <a href="https://github.com/philss/floki"target="_blank" rel="noopener">Floki</a>, to see how they are organized.</p>
</li>
</ul>
<p>Each Erlang VM (called BEAM) is a single OS process, that manages a single real thread per CPU core available in your machine. Each real thread is managed by its own BEAM Scheduler, which will slice processing time between the lightweight processes inside.</p>
<p>Each BEAM process has its own mailbox to receive messages (more correctly called a <strong>run-queue</strong>). I/O operations such as file management will run asynchronously and not block the scheduler, which will manage to run other processes while waiting for I/O.</p>
<p>Each BEAM process also has its own separated heap and garbage collector (a generational, 2 stage copy collector). Because each process has very little state (variables in a function) each garbage collector stop is super short and runs fast.</p>
<p>So, each BEAM VM can be thought of as an entire application infrastructure, with many Yocto Services available for your application to call.</p>
<p>And as I said in the <a href="http://www.akitaonrails.com/en/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir"target="_blank" rel="noopener">previous article</a>, each BEAM VM can remotely call other BEAM VMs and exchange messages between them as if they were in the same VM. The semantics are almost the same and you have distributed computing the easier way.</p>
<p>Erlang implemented a fantastic set of primitives that quickly scale as large as you want or need, with the proper wirings to not let you down. And Elixir fixes the only problem most people have with Erlang: its <a href="http://www.akitaonrails.com/en/2015/11/25/elixir-101-apresentando-a-sintaxe"target="_blank" rel="noopener">old Prolog-inspired weird syntax</a>. Elixir is a thin and effective coat of modern language design, partly inspired by Ruby (although not a port by any means, it&rsquo;s still Erlang underneath).</p>
<p>I hope this series helped shed some light on why Elixir is the best choice among the new generation of high concurrency enabled languages and I also hope I made myself clear on why just high concurrency is not enough: you want high reliability as well. And in this aspect, the OTP architecture, built-in with Erlang, has no competition.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>software-architecture</category></item><item><title>Elixir 101 - Introducing the Syntax</title><link>https://akitaonrails.github.io/en/2015/11/25/elixir-101-introducing-the-syntax/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/11/25/elixir-101-introducing-the-syntax/</guid><pubDate>Wed, 25 Nov 2015 17:25:00 -0200</pubDate><description>&lt;p&gt;I&amp;rsquo;ve been posting a lot of articles in the last few weeks, check out the &lt;a href="http://www.akitaonrails.com/elixir"target="_blank" rel="noopener"&gt;&amp;ldquo;Elixir&amp;rdquo; tag&lt;/a&gt; to read all of them.&lt;/p&gt;
&lt;p&gt;Many tutorial series start introducing a new language by its syntax. I subverted the order. Elixir is not interesting because of its syntax. Erlang is interesting all by itself, because of its very mature, highly reliable, highly concurrent, distributed nature. But its syntax is not for the faint of heart. It&amp;rsquo;s not &amp;ldquo;ugly&amp;rdquo;, it&amp;rsquo;s just too different for those of us from the C school to easily digest. It derives from Prolog, and this is one small example of a Prolog exercise:&lt;/p&gt;</description><content:encoded><![CDATA[<p>I&rsquo;ve been posting a lot of articles in the last few weeks, check out the <a href="http://www.akitaonrails.com/elixir"target="_blank" rel="noopener">&ldquo;Elixir&rdquo; tag</a> to read all of them.</p>
<p>Many tutorial series start introducing a new language by its syntax. I subverted the order. Elixir is not interesting because of its syntax. Erlang is interesting all by itself, because of its very mature, highly reliable, highly concurrent, distributed nature. But its syntax is not for the faint of heart. It&rsquo;s not &ldquo;ugly&rdquo;, it&rsquo;s just too different for those of us from the C school to easily digest. It derives from Prolog, and this is one small example of a Prolog exercise:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-prolog" data-lang="prolog"><span class="line"><span class="cl"><span class="c1">% P03 (*): Find the K&#39;th element of a list.</span>
</span></span><span class="line"><span class="cl"><span class="c1">%     The first element in the list is number 1.</span>
</span></span><span class="line"><span class="cl"><span class="c1">%     Example:</span>
</span></span><span class="line"><span class="cl"><span class="c1">%     ?- element_at(X,[a,b,c,d,e],3).</span>
</span></span><span class="line"><span class="cl"><span class="c1">%     X = c</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">% The first element in the list is number 1.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">% element_at(X,L,K) :- X is the K&#39;th element of the list L</span>
</span></span><span class="line"><span class="cl"><span class="c1">%    (element,list,integer) (?,?,+)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">% Note: nth1(?Index, ?List, ?Elem) is predefined</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">element_at</span><span class="p">(</span><span class="nv">X</span><span class="p">,[</span><span class="nv">X</span><span class="p">|</span><span class="k">_</span><span class="p">],</span><span class="mi">1</span><span class="p">).</span>
</span></span><span class="line"><span class="cl"><span class="nf">element_at</span><span class="p">(</span><span class="nv">X</span><span class="p">,[</span><span class="k">_</span><span class="p">|</span><span class="nv">L</span><span class="p">],</span><span class="nv">K</span><span class="p">)</span> <span class="p">:-</span> <span class="nv">K</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">K1</span> <span class="o">is</span> <span class="nv">K</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="nf">element_at</span><span class="p">(</span><span class="nv">X</span><span class="p">,</span><span class="nv">L</span><span class="p">,</span><span class="nv">K1</span><span class="p">).</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Erlang has a similar syntax, with the idea of phrases divided by commas and ending with a dot.</p>
<p>José Valim played it very smart: he chose the best of the available mature platforms and coated it with a layer of modern syntax and easier-to-use standard libraries. This is the same problem implemented in Elixir:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Exercise</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">element_at</span><span class="p">(</span><span class="o">[</span><span class="n">found</span><span class="o">|</span><span class="n">_</span><span class="o">]</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="n">found</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">element_at</span><span class="p">(</span><span class="o">[</span><span class="n">_</span><span class="o">|</span><span class="n">rest</span><span class="o">]</span><span class="p">,</span> <span class="n">position</span><span class="p">)</span> <span class="k">when</span> <span class="n">position</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">element_at</span><span class="p">(</span><span class="n">rest</span><span class="p">,</span> <span class="n">position</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If I copy and paste the code above in an IEx shell I can test it out like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(7)&gt; Exercise.element_at([&#34;a&#34;, &#34;b&#34;, &#34;c&#34;, &#34;d&#34;, &#34;e&#34;], 3)
&#34;c&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This simple exercise shows us some of the powerful bits of Erlang that Elixir capitalizes upon, such as pattern matching and recursion.</p>
<p>First of all, every function <strong>must</strong> be defined inside a module, which you name with <tt>defmodule My.Module do .. end</tt>. Internally it becomes the atom &ldquo;Elixir.My.Module&rdquo;. Nesting modules is just a longer name concatenated with dots.</p>
<p>Then you can define a public function with the <tt>def my_function(args) do .. end</tt> block, which is just a macro for the same <tt>def my_function(args), do: &hellip;</tt> construct. Private functions are declared with <tt>defp</tt>.</p>
<p>A function is actually identified by the pair of its name and its arity. So above we have <tt>element_at/2</tt> which means it accepts 2 arguments. But we have 2 functions with the same arity: the difference is the <strong>pattern matching</strong>.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">element_at</span><span class="p">(</span><span class="o">[</span><span class="n">found</span><span class="o">|</span><span class="n">_</span><span class="o">]</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="n">found</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Here we are saying: the first argument will be an array, decompose it. The first element of the array will be stored in the &ldquo;found&rdquo; variable, the rest &ldquo;_&rdquo; will be ignored. And the second argument must be the number &ldquo;1&rdquo;. This is the description of the so-called &ldquo;pattern&rdquo;, and it should &ldquo;match&rdquo; the input arguments received. This is <strong>&ldquo;call-by-pattern&rdquo;</strong> semantics.</p>
<p>But what if we want to pass a position other than &ldquo;1&rdquo;? That&rsquo;s why we have this second definition:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>def element_at([_|rest], position) when position &gt; 1 do</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, the first argument again <strong>needs</strong> to be an array, but this time we don&rsquo;t care about the first element, just the rest of the array without the first element. And any position other than &ldquo;1&rdquo; will be stored in the &ldquo;position&rdquo; variable.</p>
<p>But this function is special: it is <strong>guarded</strong> to only allow a <tt>position</tt> that is larger than 1. What if we try a negative position?</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(8)&gt; Exercise.element_at([&#34;a&#34;, &#34;b&#34;, &#34;c&#34;, &#34;d&#34;, &#34;e&#34;], -3)
** (FunctionClauseError) no function clause matching in Exercise.element_at/2
    iex:7: Exercise.element_at([&#34;a&#34;, &#34;b&#34;, &#34;c&#34;, &#34;d&#34;, &#34;e&#34;], -3)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It says that none of the arguments we passed match any of the clauses defined above. We could have added a third definition just to catch those cases:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">element_at</span><span class="p">(</span><span class="n">_list</span><span class="p">,</span> <span class="n">_position</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="kp">nil</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Adding the underscore &ldquo;_&rdquo; before the variable name is the same as having just the underscore, but we are naming it just to make it more readable. Any arguments passed will just be ignored. And this is the more generic case if the previous 2 don&rsquo;t match.</p>
<p>The previous line is the same as writing:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>def element_at(_list, _position) do
  nil
end</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I won&rsquo;t dive into macros for now. Just know that there is more than one way of doing things in Elixir and you can define those different ways using Erlang&rsquo;s built-in support for macros, dynamic code that is compiled at runtime. It&rsquo;s the way of doing metaprogramming in Elixir.</p>
<p>Now, going back to the implementation, the first function can still look weird; let&rsquo;s review it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">element_at</span><span class="p">(</span><span class="o">[</span><span class="n">_</span><span class="o">|</span><span class="n">rest</span><span class="o">]</span><span class="p">,</span> <span class="n">position</span><span class="p">)</span> <span class="k">when</span> <span class="n">position</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">element_at</span><span class="p">(</span><span class="n">rest</span><span class="p">,</span> <span class="n">position</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>What happens is: when we call <tt>Exercise.element_at([&ldquo;a&rdquo;, &ldquo;b&rdquo;, &ldquo;c&rdquo;, &ldquo;d&rdquo;, &ldquo;e&rdquo;], 3)</tt> the first argument will pattern match with <tt>[_|rest]</tt>. The first element &ldquo;a&rdquo; is discarded and the new list <tt>[&ldquo;b&rdquo;, &ldquo;c&rdquo;, &ldquo;d&rdquo;, &ldquo;e&rdquo;]</tt> is stored as &ldquo;<tt>rest</tt>&rdquo;.</p>
<p>Finally, we recurse the call, decrementing the &ldquo;<tt>position</tt>&rdquo; variable. So it becomes <tt>element_at([&ldquo;b&rdquo;, &ldquo;c&rdquo;, &ldquo;d&rdquo;, &ldquo;e&rdquo;], 2)</tt>. And it repeats until position becomes &ldquo;1&rdquo;, in which case the pattern matching falls to the other function defined as:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>def element_at([found|_], 1), do: found</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In this case the rest of the array is pattern matched and the first element, &ldquo;c&rdquo;, is stored in the &ldquo;<tt>found</tt>&rdquo; variable; the rest of the array is discarded. It only got here because the position matched as &ldquo;1&rdquo;, so it just returns the variable &ldquo;found&rdquo;, which contains the 3rd element of the original array, &ldquo;c&rdquo;.</p>
<p>This is all nice and fancy, but in Elixir we could have just done this other version:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Exercise</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">element_at</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">position</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="no">Enum</span><span class="o">.</span><span class="n">at</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">position</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And we are done! Several tutorials will talk about how recursion and pattern matching to decompose lists solve a lot of problems, but Elixir gives us the convenience of treating lists as Enumerables and provides us a rich <a href="http://elixir-lang.org/docs/stable/elixir/Enum.html"target="_blank" rel="noopener">Enum</a> module with very useful functions such as <tt>at/2</tt>, <tt>each/2</tt>, <tt>take/2</tt>, and so on. Just pick what you need and you&rsquo;re managing lists like a boss.</p>
<p>Oh, and by the way, there is something called a <a href="http://elixir-lang.org/getting-started/sigils.html"target="_blank" rel="noopener">Sigil</a> in Elixir. Instead of writing the List of Strings explicitly, we could have done it like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(8)&gt; ~w(a b c d e f)
[&#34;a&#34;, &#34;b&#34;, &#34;c&#34;, &#34;d&#34;, &#34;e&#34;, &#34;f&#34;]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Or, if we wanted a List of Atoms, we could do it like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(9)&gt; ~w(a b c d e f)a
[:a, :b, :c, :d, :e, :f]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Lists, Tuples and Keyword Lists<span class="hx:absolute hx:-mt-20" id="lists-tuples-and-keyword-lists"></span>
    <a href="#lists-tuples-and-keyword-lists" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Well, this was too simple. You really need the idea of pattern matching and basic types in your mind to make it flow. Let&rsquo;s get another snippet from the <a href="http://www.akitaonrails.com/en/2015/11/18/ex-manga-downloadr-um-exercicio-com-elixir"target="_blank" rel="noopener">Ex Manga Downloadr</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">parse_args</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">parse</span> <span class="o">=</span> <span class="no">OptionParser</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">args</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">switches</span><span class="p">:</span> <span class="o">[</span><span class="nb">name</span><span class="p">:</span> <span class="ss">:string</span><span class="p">,</span> <span class="ss">url</span><span class="p">:</span> <span class="ss">:string</span><span class="p">,</span> <span class="ss">directory</span><span class="p">:</span> <span class="ss">:string</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">aliases</span><span class="p">:</span> <span class="o">[</span><span class="ss">n</span><span class="p">:</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">u</span><span class="p">:</span> <span class="ss">:url</span><span class="p">,</span> <span class="ss">d</span><span class="p">:</span> <span class="ss">:directory</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="n">parse</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="o">[</span><span class="nb">name</span><span class="p">:</span> <span class="n">manga_name</span><span class="p">,</span> <span class="ss">url</span><span class="p">:</span> <span class="n">url</span><span class="p">,</span> <span class="ss">directory</span><span class="p">:</span> <span class="n">directory</span><span class="o">]</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">process</span><span class="p">(</span><span class="n">manga_name</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="o">[</span><span class="nb">name</span><span class="p">:</span> <span class="n">manga_name</span><span class="p">,</span> <span class="ss">directory</span><span class="p">:</span> <span class="n">directory</span><span class="o">]</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">process</span><span class="p">(</span><span class="n">manga_name</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="p">}</span> <span class="o">-&gt;</span> <span class="n">process</span><span class="p">(</span><span class="ss">:help</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The first part may puzzle you:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">OptionParser</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">args</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">switches</span><span class="p">:</span> <span class="o">[</span><span class="nb">name</span><span class="p">:</span> <span class="ss">:string</span><span class="p">,</span> <span class="ss">url</span><span class="p">:</span> <span class="ss">:string</span><span class="p">,</span> <span class="ss">directory</span><span class="p">:</span> <span class="ss">:string</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">aliases</span><span class="p">:</span> <span class="o">[</span><span class="ss">n</span><span class="p">:</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">u</span><span class="p">:</span> <span class="ss">:url</span><span class="p">,</span> <span class="ss">d</span><span class="p">:</span> <span class="ss">:directory</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <tt>OptionParser.parse/2</tt> receives just 2 arguments: 2 arrays. If you come from Ruby it feels like it&rsquo;s a Hash with optional brackets, translating to something similar to this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># this is wrong</span>
</span></span><span class="line"><span class="cl"><span class="no">OptionParser</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">args</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="ss">switches</span><span class="p">:</span> <span class="p">{</span><span class="nb">name</span><span class="p">:</span> <span class="ss">:string</span><span class="p">,</span> <span class="ss">url</span><span class="p">:</span> <span class="ss">:string</span><span class="p">,</span> <span class="ss">directory</span><span class="p">:</span> <span class="ss">:string</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="ss">aliases</span><span class="p">:</span> <span class="p">{</span><span class="ss">n</span><span class="p">:</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">u</span><span class="p">:</span> <span class="ss">:url</span><span class="p">,</span> <span class="ss">d</span><span class="p">:</span> <span class="ss">:directory</span><span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This works in Ruby, but it is not the case in Elixir. There are optional brackets, but not where you think they are:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># this is the correct, more explicit version</span>
</span></span><span class="line"><span class="cl"><span class="no">OptionParser</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">args</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="ss">:switches</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="o">[</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:string</span><span class="p">},</span> <span class="p">{</span><span class="ss">:url</span><span class="p">,</span> <span class="ss">:string</span><span class="p">},</span> <span class="p">{</span><span class="ss">:directory</span><span class="p">,</span> <span class="ss">:string</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">]</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="ss">aliases</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="o">[</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span><span class="ss">:n</span><span class="p">,</span> <span class="ss">:name</span><span class="p">},</span> <span class="p">{</span><span class="ss">:u</span><span class="p">,</span> <span class="ss">:url</span><span class="p">},</span> <span class="p">{</span><span class="ss">:d</span><span class="p">,</span> <span class="ss">:directory</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">]</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>WHAT!?!?</p>
<p>Yep, the second argument is actually an array with elements that are <strong>Tuples</strong> paired with an atom key and value, and some of the values are themselves arrays with tuples.</p>
<ol>
<li>
<p>in Elixir, Lists are what we usually call an Array, a Linked-List of elements. Linked-Lists, as you know from your Computer Science classes, make it easy to insert and remove elements.</p>
</li>
<li>
<p>in Elixir, Tuples are immutable fixed lists with fixed positions, with elements delimited by the brackets &ldquo;{}&rdquo;</p>
</li>
</ol>
<p>If the previous example was just too much, let&rsquo;s step back a little:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Teste</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">teste</span><span class="p">(</span><span class="n">opts</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span><span class="p">{</span><span class="ss">:hello</span><span class="p">,</span> <span class="n">world</span><span class="p">},</span> <span class="p">{</span><span class="ss">:foo</span><span class="p">,</span> <span class="n">bar</span><span class="p">}</span><span class="o">]</span> <span class="o">=</span> <span class="n">opts</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">world</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">bar</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now we can call it like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(13)&gt; Teste.teste hello: &#34;world&#34;, foo: &#34;bar&#34;
world bar</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Which is the same as calling like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(14)&gt; Teste.teste([{:hello, &#34;world&#34;}, {:foo, &#34;bar&#34;}])
world bar</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This may confuse you, but it&rsquo;s very intuitive. You can just think of this combination of Lists (&quot;[]&quot;) with Tuple elements containing a pair of atom and value (&quot;{:key, value}&quot;) as behaving almost like Ruby Hashes being used for optional named arguments.</p>
<p>Then, we have the Pattern Match section in both previous examples:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">case</span> <span class="n">parse</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="o">[</span><span class="nb">name</span><span class="p">:</span> <span class="n">manga_name</span><span class="p">,</span> <span class="ss">url</span><span class="p">:</span> <span class="n">url</span><span class="p">,</span> <span class="ss">directory</span><span class="p">:</span> <span class="n">directory</span><span class="o">]</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">process</span><span class="p">(</span><span class="n">manga_name</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="o">[</span><span class="nb">name</span><span class="p">:</span> <span class="n">manga_name</span><span class="p">,</span> <span class="ss">directory</span><span class="p">:</span> <span class="n">directory</span><span class="o">]</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">process</span><span class="p">(</span><span class="n">manga_name</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">process</span><span class="p">(</span><span class="ss">:help</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">[</span><span class="p">{</span><span class="ss">:hello</span><span class="p">,</span> <span class="n">world</span><span class="p">},</span> <span class="p">{</span><span class="ss">:foo</span><span class="p">,</span> <span class="n">bar</span><span class="p">}</span><span class="o">]</span> <span class="o">=</span> <span class="n">opts</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The last example is just decomposition. The previous example is pattern matching and decomposition. You match based on the atoms and positions within the tuples within the list. You match from the more narrow case to the more generic case. And in the process, the variables in the pattern are available for you to use in the matching case clause.</p>
<p>Let&rsquo;s understand the meaning of this line:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="p">{</span><span class="o">[</span><span class="nb">name</span><span class="p">:</span> <span class="n">manga_name</span><span class="p">,</span> <span class="ss">url</span><span class="p">:</span> <span class="n">url</span><span class="p">,</span> <span class="ss">directory</span><span class="p">:</span> <span class="n">directory</span><span class="o">]</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">process</span><span class="p">(</span><span class="n">manga_name</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It is saying: given the results of the <tt>OptionParser.parse/2</tt> function, it must be a tuple with 3 elements. The second and third elements don&rsquo;t matter. But the first element must be a List with at least 3 tuples. And the keys of each tuple must be the atoms <tt>:name</tt>, <tt>:url</tt>, and <tt>:directory</tt>. If they&rsquo;re there, store the values of each tuple in the variables <tt>manga_name</tt>, <tt>url</tt>, and <tt>directory</tt>, respectively.</p>
<p>This may really confuse you in the beginning, but this combination of a List of Tuples is what&rsquo;s called a <a href="http://elixir-lang.org/getting-started/maps-and-dicts.html#keyword-lists"target="_blank" rel="noopener"><strong>Keyword List</strong></a>, and you will find this pattern many times, so get used to it.</p>
<p>Keyword Lists feel like a Map, but a Map has a different syntax:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">list</span> <span class="o">=</span> <span class="o">[</span><span class="ss">a</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="ss">b</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="ss">c</span><span class="p">:</span> <span class="mi">3</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="n">map</span> <span class="o">=</span> <span class="sx">%{:a =&gt; 1, :b =&gt; 2, :c =&gt; 3}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This should summarize it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(1)&gt; list = [a: 1, b: 2, c: 3]
[a: 1, b: 2, c: 3]
iex(2)&gt; map = %{:a =&gt; 1, :b =&gt; 2, :c =&gt; 3}
%{a: 1, b: 2, c: 3}

iex(3)&gt; list[:a]
1
iex(4)&gt; map[:a]
1

iex(5)&gt; list.a
** (ArgumentError) argument error
    :erlang.apply([a: 1, b: 2, c: 3], :a, [])
iex(5)&gt; map.a
1

iex(6)&gt; list2 = [{:a, 1}, {:b, 2}, {:c, 3}]
[a: 1, b: 2, c: 3]
iex(7)&gt; list = list2
[a: 1, b: 2, c: 3]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Keyword Lists are convenient as function arguments or return values. But if you want to process a collection of key-value pairs, use a dictionary-like structure, in this case a Map, especially if you need to search the collection using the key. They look similar, but the internal structures are not the same. A Keyword List is not a Map; it&rsquo;s just a convenience for a static list of tuples.</p>
<p>Finally, if this pattern matches the <tt>parse</tt> variable passed to the <tt>case</tt> block, it executes the statement <tt>process(manga_name, url, directory)</tt>, passing the 3 variables captured in the match. Otherwise it proceeds to try the next pattern in the <tt>case</tt> block.</p>
<p>The idea is that the &ldquo;=&rdquo; operator is not an &ldquo;assignment&rdquo;, it&rsquo;s a matcher: you match one side with the other. Read the error message when a pattern is not matched:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(15)&gt; [a, b, c] = 1
** (MatchError) no match of right hand side value: 1</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is a matching error, not an assignment error. But if it succeeds this is what we have:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(15)&gt; [a, b, c] = [1, 2, 3]
[1, 2, 3]
iex(16)&gt; a
1
iex(17)&gt; c
3</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is List decomposition. It so happens that in the simple case it feels like a variable assignment, but it&rsquo;s much more complex than that.</p>
<h3>Pipelines<span class="hx:absolute hx:-mt-20" id="pipelines"></span>
    <a href="#pipelines" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>We use exactly those concepts of pattern matching on the elements returned from the HTML parsed by Floki in my Manga Downloadr:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Floki</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">html</span><span class="p">,</span> <span class="s2">&#34;#listing a&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">fn</span> <span class="p">{</span><span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="o">[</span><span class="p">{</span><span class="s2">&#34;href&#34;</span><span class="p">,</span> <span class="n">url</span><span class="p">}</span><span class="o">]</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">url</span> <span class="k">end</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <tt>find/2</tt> takes an HTML string from the fetched page and matches against the CSS selectors in the second argument. The result is a List of Tuples representing the structure of each HTML Node found, in this case, this pattern: <tt>{&ldquo;a&rdquo;, [{&ldquo;href&rdquo;, url}], _}</tt></p>
<p>We can then <tt>Enum.map/2</tt>. A map is a function that receives each element of a list and returns a new list with new elements. The first argument is the original list and the second argument is a function that receives each element and returns a new one.</p>
<p>One of the main features of the Elixir language that most languages don&rsquo;t have is the <strong>Pipe</strong> operator (&quot;|&gt;&quot;). It behaves almost like UNIX&rsquo;s pipe operator &ldquo;|&rdquo; in any shell.</p>
<p>In UNIX we usually do stuff like &ldquo;<tt>ps -ef | grep PROCESS | grep -v grep | awk &lsquo;{print $2}&rsquo; | xargs kill -9</tt>&rdquo;</p>
<p>This is essentially the same as doing:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ps -ef &gt; /tmp/ps.txt
grep mix /tmp/ps.txt &gt; /tmp/grep.txt
grep -v grep /tmp/grep.txt &gt; /tmp/grep2.txt
awk &#39;{print $2}&#39; /tmp/grep2.txt &gt; /tmp/awk.txt
xargs kill -9 &lt; /tmp/awk.txt</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Each UNIX process can receive something from standard input (STDIN) and output something to standard output (STDOUT). We can redirect the output using &ldquo;&gt;&rdquo;. But instead of doing all those extra steps and creating all those extra garbage temporary files, we can simply &ldquo;pipe&rdquo; the STDOUT of one command to the STDIN of the next command.</p>
<p>Elixir uses the same principles: we can simply use the returning value of a function as the <strong>first argument</strong> of the next function. So the first example of this section is the same as doing this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">results</span> <span class="o">=</span> <span class="no">Floki</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">html</span><span class="p">,</span> <span class="s2">&#34;#listing a&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">results</span><span class="p">,</span> <span class="n">fn</span> <span class="p">{</span><span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="o">[</span><span class="p">{</span><span class="s2">&#34;href&#34;</span><span class="p">,</span> <span class="n">url</span><span class="p">}</span><span class="o">]</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">url</span> <span class="k">end</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In the same ExMangaDownloadr project we have this snippet:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">process</span><span class="p">(</span><span class="n">manga_name</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">File</span><span class="o">.</span><span class="n">mkdir_p!</span><span class="p">(</span><span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">url</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">chapters</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">pages</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">images_sources</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">process_downloads</span><span class="p">(</span><span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">optimize_images</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">compile_pdfs</span><span class="p">(</span><span class="n">manga_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">finish_process</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And we just learned that it&rsquo;s the equivalent of doing the following (I&rsquo;m cheating a bit because the 3 final functions of the workflow are not transforming the input &ldquo;directory&rdquo;, just passing it through):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">process</span><span class="p">(</span><span class="n">manga_name</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">File</span><span class="o">.</span><span class="n">mkdir_p!</span><span class="p">(</span><span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">chapters</span>  <span class="o">=</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">chapters</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">pages</span>     <span class="o">=</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">pages</span><span class="p">(</span><span class="n">chapters</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">sources</span>   <span class="o">=</span> <span class="no">Workflow</span><span class="o">.</span><span class="n">images_sources</span><span class="p">(</span><span class="n">pages</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Workflow</span><span class="o">.</span><span class="n">process_downloads</span><span class="p">(</span><span class="n">sources</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Workflow</span><span class="o">.</span><span class="n">optimize_images</span><span class="p">(</span><span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Workflow</span><span class="o">.</span><span class="n">compile_pdfs</span><span class="p">(</span><span class="n">directory</span><span class="p">,</span> <span class="n">manga_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">finish_process</span><span class="p">(</span><span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Or this much uglier version that we must read in reverse:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">process</span><span class="p">(</span><span class="n">manga_name</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">File</span><span class="o">.</span><span class="n">mkdir_p!</span><span class="p">(</span><span class="n">directory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">finish_process</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="no">Workflow</span><span class="o">.</span><span class="n">compile_pdfs</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="no">Workflow</span><span class="o">.</span><span class="n">optimize_images</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="no">Workflow</span><span class="o">.</span><span class="n">images_sources</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="no">Workflow</span><span class="o">.</span><span class="n">pages</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="no">Workflow</span><span class="o">.</span><span class="n">chapters</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span> <span class="n">manga_name</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We can easily see how the Pipe Operator &ldquo;|&gt;&rdquo; makes any transformation pipeline much easier to read. Anytime you are starting from a value and passing the results through a <strong>chain of transformations</strong>, you will use this operator.</p>
<h3>Next Steps<span class="hx:absolute hx:-mt-20" id="next-steps"></span>
    <a href="#next-steps" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The concepts presented in this article are the ones I think most people will find the most challenging upon first glance. If you understand Pattern Matching and Keyword Lists, you will understand all the rest.</p>
<p>The official website offers a great <a href="http://elixir-lang.org/getting-started/introduction.html"target="_blank" rel="noopener">Getting Started</a> that you must read entirely.</p>
<p>From intuition you know most things already. You have &ldquo;do .. end&rdquo; blocks, but you may not yet know that they are just convenience macros to pass a list of statements as an argument inside a Keyword List. The following blocks are equivalent:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">if</span> <span class="kp">true</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">  <span class="n">a</span> <span class="o">+</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="kp">true</span><span class="p">,</span> <span class="k">do</span><span class="p">:</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">  <span class="n">a</span> <span class="o">+</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="kp">true</span><span class="p">,</span> <span class="o">[</span><span class="p">{</span><span class="ss">:do</span><span class="p">,</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">  <span class="n">a</span> <span class="o">+</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl"><span class="p">)}</span><span class="o">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Mind-blowing, huh? There are many macros that add syntactic sugar using the primitives behind it.</p>
<p>For the most part, Valim made the powerful Erlang primitives more accessible (Lists, Atoms, Maps, etc.) and added higher abstractions using macros (do .. end blocks, the pipe operator, keyword lists, shortcuts for anonymous functions, etc.). This precise combination is what makes Elixir very enjoyable to learn. It&rsquo;s like peeling an onion: you start with the higher abstractions and discover macros of simpler structures underneath. You see a Keyword List first and discover Lists of Tuples. You see a block and discover another Keyword List disguised by a macro. And so on.</p>
<p>So you have a low barrier to entry and you can go as deep as you want into the rabbit hole, all the way to extending the language.</p>
<p>Elixir provides a very clever language design on top of the 25-year-old mature Erlang core. This is not just clever, it&rsquo;s the intelligent choice. Keep learning!</p>
]]></content:encoded><category>tutorials</category><category>elixir-and-erlang</category><category>programming</category></item><item><title>ExMessenger Exercise: Understanding Nodes in Elixir</title><link>https://akitaonrails.github.io/en/2015/11/25/exmessenger-exercise-understanding-nodes-in-elixir/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/11/25/exmessenger-exercise-understanding-nodes-in-elixir/</guid><pubDate>Wed, 25 Nov 2015 13:19:00 -0200</pubDate><description>&lt;p&gt;I was working through this &lt;a href="http://drew.kerrigan.io/ditributed-elixir/"target="_blank" rel="noopener"&gt;old 2014 blog post&lt;/a&gt; by Drew Kerrigan where he builds a bare-bones, command-line-based chat application, with a client that sends messages and commands to a server.&lt;/p&gt;
&lt;p&gt;This is Elixir pre-1.0, and because it&amp;rsquo;s an exercise I refactored the original code and merged the server (ex_messenger) and client (ex_messenger_client) projects into an &lt;a href="http://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-apps.html"target="_blank" rel="noopener"&gt;Elixir Umbrella&lt;/a&gt; project and you can find my code on &lt;a href="https://github.com/akitaonrails/ex_messenger_exercise/blob/master/apps/ex_messenger_client/lib/ex_messenger_client.ex"target="_blank" rel="noopener"&gt;Github here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have multiple applications that work together and share the same dependencies you can use the Umbrella convention to have them all in the same codebase. If you &lt;tt&gt;mix compile&lt;/tt&gt; from the umbrella root, it compiles all the apps (which are independent Elixir mix projects as well). It&amp;rsquo;s just a way to have related apps in the same place instead of in multiple different repositories.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I was working through this <a href="http://drew.kerrigan.io/ditributed-elixir/"target="_blank" rel="noopener">old 2014 blog post</a> by Drew Kerrigan where he builds a bare-bones, command-line-based chat application, with a client that sends messages and commands to a server.</p>
<p>This is Elixir pre-1.0, and because it&rsquo;s an exercise I refactored the original code and merged the server (ex_messenger) and client (ex_messenger_client) projects into an <a href="http://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-apps.html"target="_blank" rel="noopener">Elixir Umbrella</a> project and you can find my code on <a href="https://github.com/akitaonrails/ex_messenger_exercise/blob/master/apps/ex_messenger_client/lib/ex_messenger_client.ex"target="_blank" rel="noopener">Github here</a>.</p>
<p>If you have multiple applications that work together and share the same dependencies you can use the Umbrella convention to have them all in the same codebase. If you <tt>mix compile</tt> from the umbrella root, it compiles all the apps (which are independent Elixir mix projects as well). It&rsquo;s just a way to have related apps in the same place instead of in multiple different repositories.</p>
<p>The code shown here is in <a href="https://github.com/akitaonrails/ex_messenger_exercise"target="_blank" rel="noopener">my personal Github repository</a> if you want to clone it.</p>
<h3>Nodes 101<span class="hx:absolute hx:-mt-20" id="nodes-101"></span>
    <a href="#nodes-101" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Before we check out the exercise, there is one more concept I need to clear up. In the previous article I explained how you can start processes and exchange messages, and how you can use the OTP GenServer and Supervisor to create more robust and fault-tolerant processes.</p>
<p>But this is just the beginning of the story. You&rsquo;ve probably heard how Erlang is great for distributed computing as well. Each Erlang VM (or BEAM) is network-enabled.</p>
<p>Again, this is one more concept I am still just beginning to properly learn, and you will want to read Elixir&rsquo;s website documentation on <a href="http://elixir-lang.org/getting-started/mix-otp/distributed-tasks-and-configuration.html"target="_blank" rel="noopener">Distributed tasks and configuration</a>, that does an excellent job explaining how all this works.</p>
<p>But just to get started you can simply start 2 IEx sessions. From one terminal you can do:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex --sname fabio --cookie chat

Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.1.1) - press Ctrl&#43;C to exit (type h() ENTER for help)
iex(fabio@Hal9000u)1&gt; </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And from a different terminal you can do:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex --sname akita --cookie chat

Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.1.1) - press Ctrl&#43;C to exit (type h() ENTER for help)
iex(akita@Hal9000u)1&gt; </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice how the IEx shell shows different Node names for each instance: &ldquo;fabio@Hal9000u&rdquo; and &ldquo;akita@Hal9000u&rdquo;. It&rsquo;s the sname concatenated with your machine name. From one instance you can ping the other, for example:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(akita@Hal9000u)2&gt; Node.ping(:&#34;fabio@Hal9000u&#34;)
:pong</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If the name is correct and the other instance is indeed up, it responds to the ping with a <tt>:pong</tt>. This works only for nodes on the same machine, but what if I need to connect to an instance on a remote machine?</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(akita@Hal9000u)3&gt; Node.ping(:&#34;fabio@192.168.1.13&#34;)

11:02:46.152 [error] ** System NOT running to use fully qualified hostnames **
** Hostname 192.168.1.13 is illegal **</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <tt>&ndash;sname</tt> option sets a name only reachable within the same subnet; for a fully qualified domain name you need to use <tt>&ndash;name</tt>, for example like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex  --name fabio@192.168.1.13 --cookie chat</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And for the other node:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex --name akita@192.168.1.13 --cookie chat</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And from this second terminal you can ping the other node the same way as before:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(akita@192.168.1.13)1&gt; Node.ping(:&#34;fabio@192.168.1.13&#34;)
:pong</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And you might be wondering, what is this &ldquo;<tt>&ndash;cookie</tt>&rdquo; thing? Just spin up a third terminal with another client name, but without the cookie, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex --name john@192.168.1.13</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And if you try to ping one of the first two nodes you won&rsquo;t get a <tt>:pong</tt> back:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(john@192.168.1.13)1&gt; Node.ping(:&#34;fabio@192.168.1.13&#34;)
:pang</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The cookie is just an atom to identify relationships between nodes. In a pool of several servers you can make sure you&rsquo;re not trying to connect different applications to each other. And as a result you get a <tt>:pang</tt>. Instead of an IP address you can use a fully qualified domain name.</p>
<p>And just by having the node &ldquo;akita@&rdquo; pinging &ldquo;fabio@&rdquo; we can see that they are aware of each other:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(fabio@192.168.1.13)2&gt; Node.list
[:&#34;akita@192.168.1.13&#34;]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(akita@192.168.1.13)2&gt; Node.list
[:&#34;fabio@192.168.1.13&#34;]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If one of the nodes crashes or quits, the Node list is automatically refreshed to reflect only nodes that are actually alive and responding.</p>
<p>You can check the official API Reference for the <a href="http://elixir-lang.org/docs/stable/elixir/Node.html#summary"target="_blank" rel="noopener">Node</a> for more information. But this should give you a hint for the next section.</p>
<h3>Creating a Chat Client<span class="hx:absolute hx:-mt-20" id="creating-a-chat-client"></span>
    <a href="#creating-a-chat-client" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Back to the exercise: the ExMessenger server has &ldquo;ExMessenger.Server&rdquo;, which is a GenServer, and the &ldquo;ExMessenger.Supervisor&rdquo; that starts it up. The ExMessenger.Server is globally registered as <tt>:message_server</tt>, started and supervised by the &ldquo;ExMessenger.Supervisor&rdquo;.</p>
<p>The &ldquo;ExMessengerClient&rdquo; starts up the unsupervised &ldquo;ExMessengerClient.MessageHandler&rdquo;, which is also a GenServer, and globally registered as <tt>:message_handler</tt>.</p>
<p>The Tree for both apps looks roughly like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ExMessenger
- ExMessenger.Supervisor
    &#43; ExMessenger.Server
ExMessengerClient
- ExMessengerClient.MessageHandler</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We start them separately, first the message server:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>cd apps/ex_messenger
iex --sname server --cookie chocolate-chip -S mix run</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice that for this example we are starting with a simple name &ldquo;server&rdquo;, for the local subnet, and a cookie. It will respond as &ldquo;server@Hal9000u&rdquo; (Hal9000u being my local machine&rsquo;s name).</p>
<p>Then, we can start the client app:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>cd apps/ex_messenger_client
server=server@Hal9000u nick=john elixir --sname client -S mix run</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Here we are setting 2 environment variables (that we can retrieve inside the app using <tt>System.get_env/1</tt>) and also setting a local node name &ldquo;client&rdquo;. You can spin up more client nodes using a different &ldquo;sname&rdquo; and a different &ldquo;nick&rdquo; from another terminal, as many as you want, all linking to the same &ldquo;server@Hal9000u&rdquo; message server.</p>
<p>I&rsquo;m starting up like this instead of using a command-line escript like I did in ExMangaDownloadr because I didn&rsquo;t find any way to set the <tt>&ndash;sname</tt> or <tt>&ndash;name</tt> the same way I can set <tt>&ndash;cookie</tt> using <tt>Node.set_cookie</tt>. If anyone knows how to set it up differently, let me know in the comments section down below.</p>
<p>Notice that I said &ldquo;linking&rdquo; and not &ldquo;connecting&rdquo;. From the &ldquo;ExMessengerClient&rdquo; we start like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessengerClient</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Application</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">CLI</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">ServerProcotol</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start</span><span class="p">(</span><span class="n">_type</span><span class="p">,</span> <span class="n">_args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get_env</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">connect</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">start_message_handler</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">join_chatroom</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="no">CLI</span><span class="o">.</span><span class="n">input_loop</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <tt>get_env</tt> private function is just a wrapper to handle the &ldquo;server&rdquo; and &ldquo;nick&rdquo; environment variables that we passed:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">get_env</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">server</span> <span class="o">=</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;server&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">rstrip</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">to_atom</span>
</span></span><span class="line"><span class="cl">  <span class="n">nick</span> <span class="o">=</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;nick&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">rstrip</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, we try to connect to the remote server:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">connect</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Connecting to </span><span class="si">#{</span><span class="n">server</span><span class="si">}</span><span class="s2"> from </span><span class="si">#{</span><span class="no">Node</span><span class="o">.</span><span class="n">self</span><span class="si">}</span><span class="s2"> ...&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="no">Node</span><span class="o">.</span><span class="n">set_cookie</span><span class="p">(</span><span class="no">Node</span><span class="o">.</span><span class="n">self</span><span class="p">,</span> <span class="ss">:&#34;chocolate-chip&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="no">Node</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="kp">true</span> <span class="o">-&gt;</span> <span class="ss">:ok</span>
</span></span><span class="line"><span class="cl">    <span class="n">reason</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Could not connect to server, reason: </span><span class="si">#{</span><span class="n">reason</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">System</span><span class="o">.</span><span class="n">halt</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The important piece here is that we are setting the client instance&rsquo;s cookie with <tt>Node.set_cookie/1</tt> (notice that we didn&rsquo;t pass it in the command-line options like we did with the server instance). Without setting the cookie, the next line with <tt>Node.connect(server)</tt> would fail to connect, as I explained in the previous section.</p>
<p>Then, we start the &ldquo;ExMessengerClient.MessageHandler&rdquo; GenServer, linking with the Message Server instance:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">start_message_handler</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">MessageHandler</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="n">server</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Connected&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The Message Handler GenServer itself is very simple. It just sets the server as the state, handles incoming messages from the server and prints them out in the client&rsquo;s terminal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">MessageHandler</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">GenServer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_link</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:gen_server</span><span class="o">.</span><span class="n">start_link</span><span class="p">({</span> <span class="ss">:local</span><span class="p">,</span> <span class="ss">:message_handler</span> <span class="p">},</span> <span class="n">__MODULE__</span><span class="p">,</span> <span class="n">server</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="n">server</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_cast</span><span class="p">({</span> <span class="ss">:message</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span> <span class="p">},</span> <span class="n">server</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="n">message</span> <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">rstrip</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="si">#{</span><span class="n">server</span><span class="si">}</span><span class="s2">&gt; </span><span class="si">#{</span><span class="n">nick</span><span class="si">}</span><span class="s2">: </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">write</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">Node</span><span class="o">.</span><span class="n">self</span><span class="si">}</span><span class="s2">&gt; &#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="n">server</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Going back to the main &ldquo;ExMessengerClient&rdquo; module, after starting the (unsupervised) GenServer that receives incoming messages, we proceed to join the pseudo-chatroom in the server:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">join_chatroom</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">connect</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">users</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;* Joined the chatroom *&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;* Users in the room: </span><span class="si">#{</span><span class="n">users</span><span class="si">}</span><span class="s2"> *&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;* Type /help for options *&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">reason</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Could not join chatroom, reason: </span><span class="si">#{</span><span class="n">reason</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">System</span><span class="o">.</span><span class="n">halt</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I defined this &ldquo;ServerProcotol&rdquo; module, which is just a convenience wrapper for <tt>GenServer.call/3</tt> and <tt>GenServer.cast/2</tt> calls, to send messages to the remote GenServer called <tt>:message_server</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">ServerProcotol</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">connect</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">call</span><span class="p">({</span><span class="ss">:connect</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">disconnect</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">call</span><span class="p">({</span><span class="ss">:disconnect</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">list_users</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">({</span><span class="ss">:list_users</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">private_message</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">},</span> <span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">({</span><span class="ss">:private_message</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">say</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">},</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">({</span><span class="ss">:say</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">call</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">({</span><span class="ss">:message_server</span><span class="p">,</span> <span class="n">server</span><span class="p">},</span> <span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">cast</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">GenServer</span><span class="o">.</span><span class="n">cast</span><span class="p">({</span><span class="ss">:message_server</span><span class="p">,</span> <span class="n">server</span><span class="p">},</span> <span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pretty straightforward. Then, the main ExMessengerClient calls the recursive <tt>input_loop/1</tt> function from the CLI module, which just receives user input and handles the proper commands using pattern matching, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">CLI</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">ServerProcotol</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">input_loop</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">write</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">Node</span><span class="o">.</span><span class="n">self</span><span class="si">}</span><span class="s2">&gt; &#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">line</span> <span class="o">=</span> <span class="no">IO</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="ss">:line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">rstrip</span>
</span></span><span class="line"><span class="cl">    <span class="n">handle_command</span> <span class="n">line</span><span class="p">,</span> <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">input_loop</span> <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/help&#34;</span><span class="p">,</span> <span class="n">_args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Available commands:
</span></span></span><span class="line"><span class="cl"><span class="s2">      /leave
</span></span></span><span class="line"><span class="cl"><span class="s2">      /join
</span></span></span><span class="line"><span class="cl"><span class="s2">      /users
</span></span></span><span class="line"><span class="cl"><span class="s2">      /pm &lt;to nick&gt; &lt;message&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">      or just type a message to send
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/leave&#34;</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">disconnect</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;You have exited the chatroom, you can rejoin with /join or quit with /quit&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/quit&#34;</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">disconnect</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">System</span><span class="o">.</span><span class="n">halt</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/join&#34;</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Joined the chatroom&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/users&#34;</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">list_users</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">_args</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="ss">:ok</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="kp">nil</span><span class="p">,</span> <span class="n">_args</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="ss">:ok</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">String</span><span class="o">.</span><span class="n">contains?</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="s2">&#34;/pm&#34;</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">}</span> <span class="o">=</span> <span class="n">parse_private_recipient</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">private_message</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">say</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">parse_private_recipient</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span><span class="n">to</span><span class="o">|</span><span class="n">message</span><span class="o">]</span> <span class="o">=</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">slice</span><span class="p">(</span><span class="mi">4</span><span class="o">..-</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">split</span>
</span></span><span class="line"><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="no">List</span><span class="o">.</span><span class="n">foldl</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">fn</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">acc</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">acc</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">x</span><span class="si">}</span><span class="s2">&#34;</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">lstrip</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this wraps up the Client.</p>
<h3>Creating a Chat Server<span class="hx:absolute hx:-mt-20" id="creating-a-chat-server"></span>
    <a href="#creating-a-chat-server" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The Chat Client sends GenServer messages to a remote <tt>{:message_server, server}</tt>, and in the example, <tt>server</tt> is just the sname &ldquo;server@Hal9000u&rdquo; atom.</p>
<p>Now, we need this <tt>:message_server</tt> and this is the &ldquo;ExMessenger.Server&rdquo; GenServer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessenger</span><span class="o">.</span><span class="n">Server</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">GenServer</span>
</span></span><span class="line"><span class="cl">  <span class="nb">require</span> <span class="no">Logger</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_link</span><span class="p">(</span><span class="o">[]</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:gen_server</span><span class="o">.</span><span class="n">start_link</span><span class="p">({</span> <span class="ss">:local</span><span class="p">,</span> <span class="ss">:message_server</span> <span class="p">},</span> <span class="n">__MODULE__</span><span class="p">,</span> <span class="o">[]</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="o">[]</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="no">HashDict</span><span class="o">.</span><span class="n">new</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And that&rsquo;s it. When the &ldquo;ExMessenger.Supervisor&rdquo; starts this GenServer it registers it globally in this instance as <tt>:message_server</tt>. And this is how we address messages from what we called &ldquo;clients&rdquo; (the ExMessengerClient application).</p>
<p>When the ExMessengerClient calls the <tt>ServerProtocol.connect/1</tt>, it sends the <tt>{:connect, nick}</tt> message to the server. In the Server we handle it like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">handle_call</span><span class="p">({</span> <span class="ss">:connect</span><span class="p">,</span> <span class="n">nick</span> <span class="p">},</span> <span class="p">{</span><span class="n">from</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="p">,</span> <span class="n">users</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">cond</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">nick</span> <span class="o">==</span> <span class="ss">:server</span> <span class="ow">or</span> <span class="n">nick</span> <span class="o">==</span> <span class="s2">&#34;server&#34;</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="ss">:nick_not_allowed</span><span class="p">,</span> <span class="n">users</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="no">HashDict</span><span class="o">.</span><span class="n">has_key?</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">nick</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="ss">:nick_in_use</span><span class="p">,</span> <span class="n">users</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kp">true</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">new_users</span> <span class="o">=</span> <span class="n">users</span> <span class="o">|&gt;</span> <span class="no">HashDict</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">nick</span><span class="p">,</span> <span class="n">node</span><span class="p">(</span><span class="n">from</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="n">user_list</span> <span class="o">=</span> <span class="n">log</span><span class="p">(</span><span class="n">new_users</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="s2">&#34;has joined&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="n">user_list</span> <span class="p">},</span> <span class="n">new_users</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>First, it checks if the nick is &ldquo;server&rdquo; and disallows it. Second, it checks if the nickname already exists in the internal <a href="http://elixir-lang.org/docs/stable/elixir/HashDict.html"target="_blank" rel="noopener">HashDict</a> (a key/value dictionary) and refuses if it already exists. Finally, third, it puts the pair of nickname and node name (like &ldquo;client@Hal9000u&rdquo;) in the HashDict and broadcasts through the <tt>log/3</tt> private function to all other nodes in the HashDict dictionary.</p>
<p>The <tt>log/3</tt> just creates a log message concatenating the nicknames of all clients and prints it out, then broadcasts it to the Message Handler of all the clients listed in the HashDict:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">log</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">user_list</span> <span class="o">=</span> <span class="n">users</span> <span class="o">|&gt;</span> <span class="no">HashDict</span><span class="o">.</span><span class="n">keys</span> <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="n">nick</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">, user_list: </span><span class="si">#{</span><span class="n">user_list</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">say</span><span class="p">(</span><span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">user_list</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">say</span><span class="p">(</span><span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">GenServer</span><span class="o">.</span><span class="n">cast</span><span class="p">(</span><span class="ss">:message_server</span><span class="p">,</span> <span class="p">{</span> <span class="ss">:say</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="s2">&#34;* </span><span class="si">#{</span><span class="n">nick</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2"> *&#34;</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">handle_cast</span><span class="p">({</span> <span class="ss">:say</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span> <span class="p">},</span> <span class="n">users</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">ears</span> <span class="o">=</span> <span class="no">HashDict</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">nick</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="n">nick</span><span class="si">}</span><span class="s2"> said </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">broadcast</span><span class="p">(</span><span class="n">ears</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="n">users</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Up to this point it just casts a message to itself, the <tt>{:say, nick, message}</tt> tuple, which is handled by the GenServer and calls the <tt>broadcast/3</tt> function defined like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">broadcast</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">fn</span> <span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="n">node</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="no">Task</span><span class="o">.</span><span class="n">async</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">send_message_to_client</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Task</span><span class="o">.</span><span class="n">await</span><span class="o">/</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">send_message_to_client</span><span class="p">(</span><span class="n">client_node</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">GenServer</span><span class="o">.</span><span class="n">cast</span><span class="p">({</span> <span class="ss">:message_handler</span><span class="p">,</span> <span class="n">client_node</span> <span class="p">},</span> <span class="p">{</span> <span class="ss">:message</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It maps the list of users and fires up an asynchronous Elixir <tt>Task</tt> (which is itself just a GenServer, as I explained before in the Ex Manga Downloader series). Because it&rsquo;s a broadcast, it makes sense to make all of them parallel.</p>
<p>The important bit is the <tt>send_message_to_client/3</tt> which casts a message to the tuple <tt>{ :message_handler, client_node }</tt> where &ldquo;client_node&rdquo; is just &ldquo;client@Hal9000u&rdquo; or whatever &ldquo;&ndash;sname&rdquo; you used to start up each client node.</p>
<p>Now, this is how the clients send GenServer message calls/casts to <tt>{:message_server, server}</tt> and it sends messages back to <tt>{:message_handler, client}</tt>.</p>
<h3>This is not your traditional TCP Client/Server example!<span class="hx:absolute hx:-mt-20" id="this-is-not-your-traditional-tcp-clientserver-example"></span>
    <a href="#this-is-not-your-traditional-tcp-clientserver-example" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Now, we are calling the &ldquo;ExMessenger.Server&rdquo; a Chat &ldquo;Server&rdquo; and the &ldquo;ExMessengerClient&rdquo; a Chat &ldquo;Client&rdquo;. Although we have been calling them &ldquo;Server&rdquo; and &ldquo;Client&rdquo;, they don&rsquo;t refer to the usual &ldquo;TCP Server&rdquo; and &ldquo;TCP Client&rdquo; examples you may be familiar with!</p>
<p>The ExMessenger.Server is indeed a Server (an OTP GenServer) but the ExMessengerClient.MessageHandler is also a Server (another OTP GenServer)! Because they <strong>both</strong> have Node behavior, it&rsquo;s more like they are 2 peer-to-peer nodes instead of your old-school, simple client-server relationship. They can have client behavior (the Server sends messages to the MessageHandler) and server behavior (the Server receives messages from ExMessengerClient).</p>
<p>Let this concept sink in for a moment: built into the language you get a full-blown, easy-to-use, peer-to-peer network distribution model. You don&rsquo;t need to have one single node elected as the sole &ldquo;master&rdquo;; you could have all nodes in a ring coordinating between them, avoiding single points of failure.</p>
<p>I believe this is maybe how Erlang-based services such as <a href="http://manpages.ubuntu.com/manpages/hardy/man8/ejabberd.8.html"target="_blank" rel="noopener">ejabberd</a> and <a href="https://www.rabbitmq.com/clustering.html"target="_blank" rel="noopener">RabbitMQ</a> work.</p>
<p>In the <a href="https://github.com/processone/ejabberd/blob/master/src/ejabberd_cluster.erl"target="_blank" rel="noopener">case of ejabberd</a>, I can see that it keeps the state of the cluster in Mnesia tables (Mnesia being another component of OTP, a built-in distributed NoSQL database!) and it indeed uses the Node facilities to coordinate distributed nodes:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="nf">join</span><span class="p">(</span><span class="nv">Node</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="p">{</span><span class="nb">node</span><span class="p">(),</span> <span class="nn">net_adm</span><span class="p">:</span><span class="nf">ping</span><span class="p">(</span><span class="nv">Node</span><span class="p">)}</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="nv">Node</span><span class="p">,</span> <span class="p">_}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="n">error</span><span class="p">,</span> <span class="p">{</span><span class="n">not_master</span><span class="p">,</span> <span class="nv">Node</span><span class="p">}};</span>
</span></span><span class="line"><span class="cl">        <span class="p">{_,</span> <span class="n">pong</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nn">application</span><span class="p">:</span><span class="nf">stop</span><span class="p">(</span><span class="n">ejabberd</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">application</span><span class="p">:</span><span class="nf">stop</span><span class="p">(</span><span class="n">mnesia</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">mnesia</span><span class="p">:</span><span class="nf">delete_schema</span><span class="p">([</span><span class="nb">node</span><span class="p">()]),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">application</span><span class="p">:</span><span class="nf">start</span><span class="p">(</span><span class="n">mnesia</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">mnesia</span><span class="p">:</span><span class="nf">change_config</span><span class="p">(</span><span class="n">extra_db_nodes</span><span class="p">,</span> <span class="p">[</span><span class="nv">Node</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">mnesia</span><span class="p">:</span><span class="nf">change_table_copy_type</span><span class="p">(</span><span class="n">schema</span><span class="p">,</span> <span class="nb">node</span><span class="p">(),</span> <span class="n">disc_copies</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nb">spawn</span><span class="p">(</span><span class="k">fun</span><span class="p">()</span>  <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nn">lists</span><span class="p">:</span><span class="nf">foreach</span><span class="p">(</span><span class="k">fun</span><span class="p">(</span><span class="nv">Table</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                            <span class="nv">Type</span> <span class="o">=</span> <span class="n">call</span><span class="p">(</span><span class="nv">Node</span><span class="p">,</span> <span class="n">mnesia</span><span class="p">,</span> <span class="n">table_info</span><span class="p">,</span> <span class="p">[</span><span class="nv">Table</span><span class="p">,</span> <span class="n">storage_type</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">                            <span class="nn">mnesia</span><span class="p">:</span><span class="nf">add_table_copy</span><span class="p">(</span><span class="nv">Table</span><span class="p">,</span> <span class="nb">node</span><span class="p">(),</span> <span class="nv">Type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="k">end</span><span class="p">,</span> <span class="nn">mnesia</span><span class="p">:</span><span class="nb">system_info</span><span class="p">(</span><span class="n">tables</span><span class="p">)</span><span class="o">--</span><span class="p">[</span><span class="n">schema</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">                <span class="k">end</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">application</span><span class="p">:</span><span class="nf">start</span><span class="p">(</span><span class="n">ejabberd</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="n">error</span><span class="p">,</span> <span class="p">{</span><span class="n">no_ping</span><span class="p">,</span> <span class="nv">Node</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">.</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is what a snippet of pure Erlang source code looks like, by the way. You should have enough Elixir in your head right now to be able to abstract away the ugly Erlang syntax and see that it&rsquo;s a <tt>case</tt> pattern matching on the <tt>{_, :pong}</tt> tuple, using Node&rsquo;s ping facilities to assert the connectivity of the node and updating the Mnesia table and other setups.</p>
<p>Also in the <a href="https://github.com/rabbitmq/rabbitmq-server/blob/6f70dcbe05dbba35f7d950674d293a4c7d867d44/src/rabbit_control_main.erl"target="_blank" rel="noopener">source code of the RabbitMQ-Server</a> you will find a similar thing:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nf">become</span><span class="p">(</span><span class="nv">BecomeNode</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">error_logger</span><span class="p">:</span><span class="nf">tty</span><span class="p">(</span><span class="n">false</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ok</span> <span class="o">=</span> <span class="nn">net_kernel</span><span class="p">:</span><span class="nf">stop</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nn">net_adm</span><span class="p">:</span><span class="nf">ping</span><span class="p">(</span><span class="nv">BecomeNode</span><span class="p">)</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">        <span class="n">pong</span> <span class="o">-&gt;</span> <span class="nb">exit</span><span class="p">({</span><span class="n">node_running</span><span class="p">,</span> <span class="nv">BecomeNode</span><span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="n">pang</span> <span class="o">-&gt;</span> <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;  * Impersonating node: </span><span class="si">~s</span><span class="s">...&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">BecomeNode</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="p">_}</span> <span class="o">=</span> <span class="nn">rabbit_cli</span><span class="p">:</span><span class="nf">start_distribution</span><span class="p">(</span><span class="nv">BecomeNode</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34; done</span><span class="si">~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[]),</span>
</span></span><span class="line"><span class="cl">                <span class="nv">Dir</span> <span class="o">=</span> <span class="nn">mnesia</span><span class="p">:</span><span class="nb">system_info</span><span class="p">(</span><span class="n">directory</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;  * Mnesia directory  : </span><span class="si">~s~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">Dir</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">.</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Again, pinging nodes, using Mnesia for the server state. Erlang&rsquo;s syntax is uncommon for most of us: variables start with a capitalized letter (we intuitively think they&rsquo;re constants instead), statements end with a dot, instead of dot-notation to call a function from a module it uses a colon &ldquo;:&rdquo;, unlike Elixir the parentheses are not optional, and so on. Trying to read code like this shows the value of having Elixir to unleash Erlang&rsquo;s hidden powers.</p>
<p>So, up to this point, you know how processes are spawned internally, how they are orchestrated within the OTP framework, and now how they can interact remotely through the <strong>Node</strong> peer-to-peer abstraction. And again, this is all built into the language. No other language comes even close.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>software-architecture</category></item><item><title>Observing Processes in Elixir - The Little Elixir &amp; OTP Guidebook</title><link>https://akitaonrails.github.io/en/2015/11/22/observing-processes-in-elixir-the-little-elixir-otp-guidebook/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/11/22/observing-processes-in-elixir-the-little-elixir-otp-guidebook/</guid><pubDate>Sun, 22 Nov 2015 14:57:00 -0200</pubDate><description>&lt;p&gt;In my journey to really understand how a proper Elixir application should be written, I am working through Benjamin Tan Wei Hao&amp;rsquo;s excellent &lt;a href="https://www.manning.com/books/the-little-elixir-and-otp-guidebook"target="_blank" rel="noopener"&gt;The Little Elixir &amp;amp; OTP Guidebook&lt;/a&gt;. If you&amp;rsquo;re just getting started, this is a no-brainer: buy and study this guidebook. Yes, it will help if you&amp;rsquo;ve already read Dave Thomas&amp;rsquo; &lt;a href="https://pragprog.com/book/elixir/programming-elixir"target="_blank" rel="noopener"&gt;Programming Elixir&lt;/a&gt; book first.&lt;/p&gt;
&lt;p&gt;In my &lt;a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener"&gt;Ex Manga Downloadr Part 2&lt;/a&gt; article I explored adding better process pool control using the excellent and robust Poolboy library. One of the guidebook&amp;rsquo;s main exercises is to build a simpler version of Poolboy in pure Elixir (Poolboy is written in good, old, Erlang).&lt;/p&gt;</description><content:encoded><![CDATA[<p>In my journey to really understand how a proper Elixir application should be written, I am working through Benjamin Tan Wei Hao&rsquo;s excellent <a href="https://www.manning.com/books/the-little-elixir-and-otp-guidebook"target="_blank" rel="noopener">The Little Elixir &amp; OTP Guidebook</a>. If you&rsquo;re just getting started, this is a no-brainer: buy and study this guidebook. Yes, it will help if you&rsquo;ve already read Dave Thomas&rsquo; <a href="https://pragprog.com/book/elixir/programming-elixir"target="_blank" rel="noopener">Programming Elixir</a> book first.</p>
<p>In my <a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener">Ex Manga Downloadr Part 2</a> article I explored adding better process pool control using the excellent and robust Poolboy library. One of the guidebook&rsquo;s main exercises is to build a simpler version of Poolboy in pure Elixir (Poolboy is written in good, old, Erlang).</p>
<p>The main goal of this article is to introduce what <strong>Fault Tolerance</strong> in Erlang/Elixir means, and it is also an excuse for me to show off Erlang&rsquo;s observer:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/528/big_observer-kill.png" alt="Observer"  loading="lazy" /></p>
<p>Yes, Erlang allows us not just to see what&rsquo;s going on inside its runtime environment, but we can even take action on individual Processes running inside it! How cool is that?</p>
<p>But before we can show Fault Tolerance and the Observer I need to explain what Processes are, and why they matter. You <strong>must</strong> understand the following concepts to successfully understand Elixir programming:</p>
<ol>
<li>You don&rsquo;t have &ldquo;objects&rdquo;, which are runtime instances of classes (or prototypical objects, which are copies of other objects). Instead of &ldquo;Classes&rdquo; you have collections of functions organized in modules, with no dependency on internal state. And instead of &ldquo;objects&rdquo; we have, roughly speaking, &ldquo;processes&rdquo;. For example:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">MyProcess</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">accepting_messages</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">accepting_messages</span><span class="p">(</span><span class="n">state</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">receive</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:hello</span><span class="p">,</span> <span class="n">message</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Hello, </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">accepting_messages</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:counter</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="n">new_state</span> <span class="o">=</span> <span class="n">state</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;New state is </span><span class="si">#{</span><span class="n">new_state</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">accepting_messages</span><span class="p">(</span><span class="n">new_state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;What?&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">accepting_messages</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<ol start="2">
<li>We can execute a function inside another process. This is how we can spawn a brand new, concurrent, lightweight process:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">spawn</span> <span class="n">fn</span> <span class="o">-&gt;</span> <span class="no">MyProcess</span><span class="o">.</span><span class="n">start</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="c1">#PID&lt;0.87.0&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>When <tt>accepting_messages/1</tt> is called, it stops at the <tt>receive/0</tt> block, waiting to receive a new message. Then we can send messages like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">send</span> <span class="n">pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:hello</span><span class="p">,</span> <span class="s2">&#34;world&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="no">Hello</span><span class="p">,</span> <span class="n">world</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It receives the <tt>{:hello, &ldquo;world&rdquo;}</tt> tuple message, pattern matches the value <tt>&ldquo;world&rdquo;</tt> into the <tt>message</tt> variable, and concatenates it into the <tt>&ldquo;Hello, world&rdquo;</tt> string, which it prints out with <tt>IO.puts/1</tt>, and then recurses to itself again. We call the <tt>receive/0</tt> block again and block, waiting for further messages:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">send</span> <span class="n">pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">send</span> <span class="n">pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">send</span> <span class="n">pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">send</span> <span class="n">pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">4</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We send the <tt>{:counter}</tt> message to the same process pid again, and when it receives this message, it gets the <tt>state</tt> value from the function argument, increments it by 1, prints out the new state, and calls itself again passing the new state as the new argument. It blocks again, waiting for further messages, and each time it receives the <tt>{:counter}</tt> message, it increases the previous state by one again and recurses.</p>
<p>This is basically how we can maintain state in Elixir. If we kill this process and spawn a new one, it restarts with zero (which is what the <tt>start/0</tt> function does).</p>
<ol start="3">
<li>
<p>So, while you don&rsquo;t have &ldquo;objects&rdquo;, you do, however, have Processes. Superficially, a process behaves like an &ldquo;object&rdquo;. Be careful not to think that a Process is like a heavyweight Thread though. Erlang has its own internal scheduler that controls the concurrency, and you can load as many as 16 billion lightweight processes if your hardware allows it. Threads are super heavy, Erlang processes are super light.</p>
</li>
<li>
<p>As we saw in the example, each process has its own internal mechanism to receive messages from other processes. Those messages accumulate in an internal &ldquo;mailbox&rdquo; and you can choose to <tt>receive</tt> and pattern match through those messages, recursing to itself in order to receive new messages if you want.</p>
</li>
<li>
<p>Processes can be linked to or monitor other processes. For example, within an IEx shell, we are inside an Elixir process, so we could do:</p>
</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">self</span>
</span></span><span class="line"><span class="cl"><span class="c1">#PID&lt;0.98.0&gt;</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">spawn</span> <span class="n">fn</span> <span class="o">-&gt;</span> <span class="no">MyProcess</span><span class="o">.</span><span class="n">start</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="c1">#PID&lt;0.105.0&gt;</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">alive?</span><span class="p">(</span><span class="n">pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kp">true</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">link</span><span class="p">(</span><span class="n">pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kp">true</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>With <tt>self</tt> we can see that the current process id for the IEx shell is &ldquo;0.98.0&rdquo;. Then we spawn a process that calls <tt>MyProcess.start/0</tt>, which will block in the receive call. This new process has a different id, &ldquo;0.105.0&rdquo;.</p>
<p>We can assert that the new process is indeed alive and we can link the IEx shell with the &ldquo;0.105.0&rdquo; pid process. Now, whatever happens to this process will cascade to the shell.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="n">pid</span><span class="p">,</span> <span class="ss">:kill</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">**</span> <span class="p">(</span><span class="no">EXIT</span> <span class="n">from</span> <span class="c1">#PID&lt;0.98.0&gt;) killed</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Interactive</span> <span class="no">Elixir</span> <span class="p">(</span><span class="mi">1</span><span class="o">.</span><span class="mi">1</span><span class="o">.</span><span class="mi">1</span><span class="p">)</span> <span class="o">-</span> <span class="n">press</span> <span class="no">Ctrl</span><span class="o">+</span><span class="n">C</span> <span class="n">to</span> <span class="nb">exit</span> <span class="p">(</span><span class="n">type</span> <span class="n">h</span><span class="p">()</span> <span class="no">ENTER</span> <span class="k">for</span> <span class="n">help</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="sr">/home/</span><span class="n">akitaonrails</span><span class="o">/.</span><span class="n">iex</span><span class="o">.</span><span class="n">exs</span><span class="p">:</span><span class="mi">1</span><span class="p">:</span> <span class="ss">warning</span><span class="p">:</span> <span class="n">redefining</span> <span class="k">module</span> <span class="nn">R</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">self</span>
</span></span><span class="line"><span class="cl"><span class="c1">#PID&lt;0.109.0&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And indeed, if we forcefully send a kill message to the &ldquo;0.105.0&rdquo; process, the IEx shell is also killed in the process. IEx restarts and its new pid is &ldquo;0.109.0&rdquo; instead of the old &ldquo;0.98.0&rdquo;. By the way, this is one way a process is different from a normal object. It behaves more like an operating system process, where a crash in a process does not affect the whole system, because it does not hold external shared state that can corrupt the system&rsquo;s state.</p>
<p>The important concept is that we now have a mechanism to define a Parent Process (IEx in this example) and Children processes linked to it.</p>
<ol start="6">
<li>Parent processes don&rsquo;t need to stupidly kill themselves because their children screwed up. Instead, they can trap exits and decide what to do later:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">flag</span><span class="p">(</span><span class="ss">:trap_exit</span><span class="p">,</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kp">false</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">spawn_link</span> <span class="n">fn</span> <span class="o">-&gt;</span> <span class="no">MyProcess</span><span class="o">.</span><span class="n">start</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="c1">#PID&lt;0.118.0&gt;</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">send</span> <span class="n">pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:counter</span><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>First, we declare that the IEx shell will trap exits and not just die. Then we spawn a new process and link it. The <tt>spawn_link/1</tt> function has the same effect as <tt>spawn/1</tt> followed by <tt>Process.link/1</tt>. We can send a message to the new pid and check that it is indeed still working.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="n">pid</span><span class="p">,</span> <span class="ss">:kill</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kp">true</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">alive?</span><span class="p">(</span><span class="n">pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kp">false</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">flush</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:EXIT</span><span class="p">,</span> <span class="c1">#PID&lt;0.118.0&gt;, :killed}</span>
</span></span><span class="line"><span class="cl"><span class="ss">:ok</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now we forcefully kill the new process again, but IEx does not crash this time, as it is explicitly trapping those errors. If we check the killed pid, we can assert that it is indeed dead. But now we can also inspect IEx&rsquo;s own process mailbox (in this case, just flushing what&rsquo;s queued in the inbox) and see that it just received a message saying that its child was killed.</p>
<p>From here we could make the IEx process handle this message and decide to just mourn for its deceased child and kill itself, or move on and spawn_link a new one. We have <strong>choice</strong> in the face of disaster.</p>
<h3>OTP Workers<span class="hx:absolute hx:-mt-20" id="otp-workers"></span>
    <a href="#otp-workers" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Setting aside the grim metaphor, we learned that we have Parent and Child processes, but more importantly they can fit the roles of Supervisors and supervised Workers, respectively.</p>
<p>Workers are where we put our code. This code can have bugs, it can depend on external stuff that can make our code crash for unexpected reasons. In a normal language we would start using the dreaded <tt>try/catch</tt> blocks, which are just ugly and <strong>wrong</strong>! Don&rsquo;t catch errors in Elixir, just let it crash!</p>
<p>As I explained in my previous article, everything in Elixir ends up being a so-called &ldquo;OTP application&rdquo;. The example above is just a very simple contraption that we can expand upon. Let&rsquo;s rewrite the same thing as an OTP GenServer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">MyFancyProcess</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">GenServer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_link</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">GenServer</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">name</span><span class="p">:</span> <span class="n">__MODULE__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">## Public API</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">hello</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="p">{</span><span class="ss">:hello</span><span class="p">,</span> <span class="n">message</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">counter</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="ss">:counter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">## GenServer callbacks</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">start_counter</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">start_counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_call</span><span class="p">({</span><span class="ss">:hello</span><span class="p">,</span> <span class="n">message</span><span class="p">},</span> <span class="n">_from</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Hello, </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="ss">:noproc</span><span class="p">,</span> <span class="n">state</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_call</span><span class="p">(</span><span class="ss">:counter</span><span class="p">,</span> <span class="n">_from</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">new_state</span> <span class="o">=</span> <span class="n">state</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;New state is </span><span class="si">#{</span><span class="n">new_state</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="ss">:noproc</span><span class="p">,</span> <span class="n">new_state</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This new <tt>MyFancyProcess</tt> is essentially the same as <tt>MyProcess</tt> but with OTP GenServer on top of it. There are Public API functions and GenServer callbacks.</p>
<p>Benjamin&rsquo;s book goes to great lengths to detail every bit of what I just implemented. But for now just understand some basics:</p>
<ol>
<li>
<p>The module does &ldquo;<tt>use GenServer</tt>&rdquo; to import all the necessary GenServer bits for your convenience. In essence one of the things it will do is create that <tt>receive</tt> block we did in the first version to wait for messages.</p>
</li>
<li>
<p>The <tt>start_link/1</tt> function will create the instance of this GenServer and return the linked process. Internally it will call back to the <tt>init/1</tt> function to set the initial state of this worker. This is a flexible language; we have multiple ways of doing the same thing, and this is good. Having just a single way of writing code is boring.</p>
</li>
<li>
<p>The convention is to have one public function that calls the internal <tt>handle_call/3</tt> (for synchronous calls), <tt>handle_cast/2</tt> (for asynchronous calls), and <tt>handle_info/2</tt>. You could just call <tt>handle_call</tt> from the outside, but that&rsquo;s just ugly, so you will find this convention everywhere.</p>
</li>
</ol>
<p>Once we have this in place, we can start calling it directly:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">11</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="c1">#PID&lt;0.261.0&gt;}</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">hello</span><span class="p">(</span><span class="s2">&#34;world&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="no">Hello</span><span class="p">,</span> <span class="n">world</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">13</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">counter</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">14</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">counter</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">15</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">counter</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is much cleaner than the version where we manually <tt>spawn_link</tt> and <tt>send</tt> messages to a pid. This is all handled nicely by the GenServer underneath it. And as I said, the results are the same as the initial crude <tt>MyProcess</tt> example.</p>
<p>In fact, this convention does make us type a lot of boilerplate many times over. There is a library called <a href="https://github.com/sasa1977/exactor"target="_blank" rel="noopener">ExActor</a> that greatly simplifies a GenServer implementation, turning our previous code into something like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">MyFancyProcess</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExActor</span><span class="o">.</span><span class="n">GenServer</span><span class="p">,</span> <span class="ss">initial_state</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defcall</span> <span class="n">hello</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">state</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Hello, </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">noreply</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defcall</span> <span class="n">counter</span><span class="p">,</span> <span class="n">state</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">new_counter</span> <span class="o">=</span> <span class="n">state</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;New state is </span><span class="si">#{</span><span class="n">new_counter</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">new_state</span><span class="p">(</span><span class="n">new_counter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is way cleaner, but as we are just using IEx, I&rsquo;m not using this version for the next section. Stick with the longer version of <tt>MyFancyProcess</tt> listed at the beginning of this section!</p>
<h2>OTP Supervisor<span class="hx:absolute hx:-mt-20" id="otp-supervisor"></span>
    <a href="#otp-supervisor" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Now that we have a worker, we can create a Supervisor to supervise it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">MyFancySupervisor</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Supervisor</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_link</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">Supervisor</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">children</span> <span class="o">=</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">      <span class="n">worker</span><span class="p">(</span><span class="no">MyFancyProcess</span><span class="p">,</span> <span class="o">[</span><span class="mi">0</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">opts</span> <span class="o">=</span> <span class="o">[</span><span class="ss">strategy</span><span class="p">:</span> <span class="ss">:one_for_one</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">supervise</span><span class="p">(</span><span class="n">children</span><span class="p">,</span> <span class="n">opts</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is just simple boilerplate that most Supervisors will have. There are many details you must learn, but for this article&rsquo;s purposes the important bits are, first, the definition of the <tt>children</tt> specification, saying that this Supervisor should start the <tt>MyFancyProcess</tt> GenServer instead of us having to call <tt>MyFancyProcess.start_link</tt> manually. And the second important bit is the <tt>opts</tt> list which defines the strategy of <tt>:one_for_one</tt>, meaning that if the Supervisor detects that the child has died, it should restart it.</p>
<p>From a clean IEx, we can copy and paste both the <tt>MyFancyProcess</tt> and <tt>MyFancySupervisor</tt> above and start playing with it in the IEx shell:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">&gt;</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">sup_pid</span><span class="p">}</span> <span class="o">=</span> <span class="no">MyFancySupervisor</span><span class="o">.</span><span class="n">start_link</span>   
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="c1">#PID&lt;0.124.0&gt;}</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">hello</span><span class="p">(</span><span class="s2">&#34;foo&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="no">Hello</span><span class="p">,</span> <span class="n">foo</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">counter</span>     
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">counter</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is how we start the Supervisor and you can see that right away we can start sending messages to the <tt>MyFancyProcess</tt> GenServer because the Supervisor successfully started it for us.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Supervisor</span><span class="o">.</span><span class="n">count_children</span><span class="p">(</span><span class="n">sup_pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="sx">%{active: 1, specs: 1, supervisors: 0, workers: 1}</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Supervisor</span><span class="o">.</span><span class="n">which_children</span><span class="p">(</span><span class="n">sup_pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="p">{</span><span class="no">MyFancyProcess</span><span class="p">,</span> <span class="c1">#PID&lt;0.125.0&gt;, :worker, [MyFancyProcess]}]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Using the Supervisor PID that we captured right when we started it, we can ask it to count how many children it is monitoring (1, in this example) and we can ask for the details of each child as well. We can see that the <tt>MyFancyProcess</tt> started with the pid of &ldquo;0.125.0&rdquo;</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">9</span><span class="p">)</span><span class="o">&gt;</span> <span class="o">[</span><span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="n">worker_pid</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span><span class="o">]</span> <span class="o">=</span> <span class="no">Supervisor</span><span class="o">.</span><span class="n">which_children</span><span class="p">(</span><span class="n">sup_pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="p">{</span><span class="no">MyFancyProcess</span><span class="p">,</span> <span class="c1">#PID&lt;0.125.0&gt;, :worker, [MyFancyProcess]}]</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">14</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="n">worker_pid</span><span class="p">,</span> <span class="ss">:kill</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kp">true</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, we can grab the Worker pid and manually force it to crash as we did before. We should be screwed, right? Nope:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(15)&gt; Supervisor.which_children(sup_pid)                          
[{MyFancyProcess, #PID&lt;0.139.0&gt;, :worker, [MyFancyProcess]}]

iex(16)&gt; MyFancyProcess.counter
New state is 1
:noproc
iex(17)&gt; MyFancyProcess.counter
New state is 2
:noproc</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If we ask the Supervisor again for the list of its children, we will see that the old &ldquo;0.125.0&rdquo; process did indeed vanish but a new one, &ldquo;0.139.0&rdquo;, was spawned in its place by the Supervisor&rsquo;s <tt>:one_for_one</tt> strategy as we defined before.</p>
<p>We can continue making calls to <tt>MyFancyProcess</tt>, but you will see that the previous state was lost and it restarts from zero. We can add state management in the GenServer using a number of different persistent storages such as the built-in <a href="http://elixir-lang.org/getting-started/mix-otp/ets.html"target="_blank" rel="noopener">ETS</a> (think of ETS as a built-in Memcache service), but I think you get the idea by now.</p>
<h3>Graphically visualizing Processes<span class="hx:absolute hx:-mt-20" id="graphically-visualizing-processes"></span>
    <a href="#graphically-visualizing-processes" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>This entire article was motivated by just this simple thing in Benjamin&rsquo;s book: by the end of page 139 you will have built a very simple pool system that is able to start 5 processes in the pool, guarded by a supervisor. And from there he goes on to show off the Observer.</p>
<p>Erlang has a built-in inspector tool called Observer. You can use the Supervisor built-in functions to inspect processes as I demonstrated before, but it&rsquo;s much cooler to see it visually. Assuming you installed Erlang Solutions properly, in Ubuntu you have to:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb &amp;&amp; sudo dpkg -i erlang-solutions_1.0_all.deb</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Only then, you can start the observer directly from the IEx shell like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>:observer.start</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And a graphical window will show up with some stats first.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/530/big_observer-system.png" alt="Observer"  loading="lazy" /></p>
<p>This is <strong>very</strong> powerful because you have insight into and control over the entire Erlang runtime! Notice that this status window even shows you &ldquo;uptime&rdquo;; it&rsquo;s like a UNIX system: it is made to stay up no matter what. Processes have their own garbage collector and they all behave nicely towards the entire system.</p>
<p>You can hook a remote Observer to remote Erlang runtimes as well, if you were wondering. Now you can jump to the Applications tab to see what the &ldquo;Pooly&rdquo; exercise looks like with 5 children under its pool:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/527/big_observer-pooly.png" alt="Pooly"  loading="lazy" /></p>
<p>Because those children are supervised with proper restart strategies, we can visually kill one of them, the one with the pid labeled &ldquo;0.389.0&rdquo;:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/528/big_observer-kill.png" alt="Kill process"  loading="lazy" /></p>
<p>And as the Observer immediately shows, the Supervisor took action, spawned a new child and added it to its pool, bringing the count back to 5:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/529/big_observer-restart.png" alt="Respawn"  loading="lazy" /></p>
<p>This is what <strong>Fault Tolerance</strong> with proper controls means when using OTP!</p>
<p>With the bits I explained in this article you should have enough concepts to finally grasp what all the Erlang high-reliability fuss is about. The basic concepts are very simple, hooking your application into OTP is also a no-brainer, and what OTP has implemented under the hood is what makes your application much more reliable.</p>
<p>There are clear guidelines on how to design your application. Who supervises what. What should happen to the application state if workers are restarted? How do you divide responsibilities between different groups of Supervisors/Children?</p>
<p>Your application is supposed to look like a Tree, a <strong>Supervision Tree</strong>, where failure in one leaf does not bring the other branches down and everything knows how to behave and how to recover, elegantly. It&rsquo;s really like a UNIX operating system: when you <tt>kill -9</tt> one process, it doesn&rsquo;t bring your system down, and if it&rsquo;s an <tt>initd</tt>-monitored service, it gets respawned.</p>
<p>Most importantly: this is not an optional feature, a 3rd-party library that you choose to use. It&rsquo;s built into Erlang; you must use it if you want to play. There is no other choice and this is the best choice. Any such pattern that is not implemented in a concurrent language, to me, represents a big failure of the language. This is Elixir&rsquo;s strength.</p>
<p>This is high-level control you won&rsquo;t find anywhere else. And we still didn&rsquo;t even talk about how OTP applications can exchange messages across the wire in really distributed systems, and how the Erlang runtime can reload code while an application is running, with zero downtime, akin to what IEx itself is capable of and how Phoenix allows development mode with code reloading! OTP gives all this for free, so it&rsquo;s well worth learning all the details.</p>
<p>We went through processes, pids, sending a kill message to a process, trapping exits, parents having child processes. It feels very similar to how UNIX works. If you know UNIX, you can easily grasp how all this fits together, including Elixir&rsquo;s pipe operator &ldquo;|&gt;&rdquo; compared to UNIX&rsquo;s own pipe &ldquo;|&rdquo;; it&rsquo;s similar.</p>
<p>Finally, The Little Elixir &amp; OTP Guidebook is a very easy-to-read, very hands-on small book. You can read it all in a couple of days and grasp everything I quickly summarized here and much more. I highly encourage you to buy it right now.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>software-engineering</category></item><item><title>Phoenix "15 Minute Blog" comparison to Ruby on Rails</title><link>https://akitaonrails.github.io/en/2015/11/20/phoenix-15-minute-blog-comparison-to-ruby-on-rails/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/11/20/phoenix-15-minute-blog-comparison-to-ruby-on-rails/</guid><pubDate>Fri, 20 Nov 2015 17:52:00 -0200</pubDate><description>&lt;p&gt;&lt;em&gt;Update 11/23/15:&lt;/em&gt; Chris McCord, Phoenix creator, just posted an article explaining why &lt;a href="https://dockyard.com/blog/2015/11/18/phoenix-is-not-rails"target="_blank" rel="noopener"&gt;&amp;ldquo;Phoenix is not Rails&amp;rdquo;&lt;/a&gt; It goes in detail in many things I described in this article and I highly recommend you read it too.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve been following the Elixir blogosphere, chances are that you stumbled upon &lt;a href="https://medium.com/@diamondgfx"target="_blank" rel="noopener"&gt;Brandon Richey&amp;rsquo;s&lt;/a&gt; take on the classic &amp;ldquo;15 minute blog&amp;rdquo;. If you didn&amp;rsquo;t you must read at least &lt;a href="https://medium.com/@diamondgfx/introduction-fe138ac6079d#.ffl48saew"target="_blank" rel="noopener"&gt;Part 1&lt;/a&gt; and &lt;a href="https://t.co/DjGDek1ZOk"target="_blank" rel="noopener"&gt;Part 2&lt;/a&gt;. It&amp;rsquo;s a very detailed tutorial that will make it easier to get your feet wet with Phoenix goodness.&lt;/p&gt;</description><content:encoded><![CDATA[<p><em>Update 11/23/15:</em> Chris McCord, Phoenix creator, just posted an article explaining why <a href="https://dockyard.com/blog/2015/11/18/phoenix-is-not-rails"target="_blank" rel="noopener">&ldquo;Phoenix is not Rails&rdquo;</a> It goes in detail in many things I described in this article and I highly recommend you read it too.</p>
<p>If you&rsquo;ve been following the Elixir blogosphere, chances are that you stumbled upon <a href="https://medium.com/@diamondgfx"target="_blank" rel="noopener">Brandon Richey&rsquo;s</a> take on the classic &ldquo;15 minute blog&rdquo;. If you didn&rsquo;t you must read at least <a href="https://medium.com/@diamondgfx/introduction-fe138ac6079d#.ffl48saew"target="_blank" rel="noopener">Part 1</a> and <a href="https://t.co/DjGDek1ZOk"target="_blank" rel="noopener">Part 2</a>. It&rsquo;s a very detailed tutorial that will make it easier to get your feet wet with Phoenix goodness.</p>
<p>This post is intended for Rails programmers wanting to know how the Phoenix Framework compares. It&rsquo;s not a completely fair comparison as this is just the good old nested resources &ldquo;hello world&rdquo; exercise. This will only scratch the surface but should serve as a good introduction.</p>
<p>For better or for worse, many still consider Ruby on Rails to be the best DSL for a web application. Rails was successful in creating a very recognizable vocabulary to describe each component of a web project. And I will argue that one of Phoenix&rsquo;s strengths is to successfully borrow the same metaphor. This definitely makes the learning curve much smoother.</p>
<p>If you just want to clone the exercises and jump right into the code itself, I have the original <a href="https://github.com/akitaonrails/pxblog_phoenix_exercise"target="_blank" rel="noopener">Pxblog in Phoenix</a> and the comparison <a href="https://github.com/akitaonrails/pxblog_rails_exercise"target="_blank" rel="noopener">Pxblog in Rails</a>.</p>
<h3>Getting Started: command line and folder structure<span class="hx:absolute hx:-mt-20" id="getting-started-command-line-and-folder-structure"></span>
    <a href="#getting-started-command-line-and-folder-structure" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Without further ado, let&rsquo;s start comparing the basic console commands:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>rails new pxblog
rails g scaffold Post title:string body:string
rails g scaffold User username:string email:string password_digest:string
rake db:create
rake db:migrate
rails g migration AddUserIdToPosts
rails server</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix phoenix.new pxblog
mix phoenix.gen.html Post posts title:string body:string
mix phoenix.gen.html User users username:string email:string password_digest:string
mix ecto.create
mix ecto.migrate
mix ecto.gen.migration add_user_id_to_posts
mix phoenix.server
# iex -S mix phoenix.server to start within IEx and be able to IEx.pry</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Right off the bat we feel at home. In the Rails world we have both the <tt>rails</tt> command competing with traditional <tt>rake</tt> task managers. At the Phoenix side, they fortunately concentrated everything under the Elixir built-in <tt>mix</tt> command. There seem to be discussions about moving the Rails command tasks to Rake where they belong, but it&rsquo;s not coming soon.</p>
<p>As it is all under Mix territory, you can list Phoenix-related tasks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ mix help | grep -i phoenix

mix phoenix.digest      # Digests and compress static files
mix phoenix.gen.channel # Generates a Phoenix channel
mix phoenix.gen.html    # Generates controller, model and views for an HTML based resource
mix phoenix.gen.json    # Generates a controller and model for a JSON based resource
mix phoenix.gen.model   # Generates an Ecto model
mix phoenix.gen.secret  # Generates a secret
mix phoenix.new         # Create a new Phoenix v1.0.3 application
mix phoenix.routes      # Prints all routes
mix phoenix.server      # Starts applications and their servers</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The directory structure that <tt>phoenix.new</tt> generates is slightly different from Rails though:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>/_build              # binary stuff mix compiles (ignore)
/config
- config.exs         # think Rails&#39; /config/application.rb
- dev.exs            # think Rails&#39; /config/environments/development.rb
- prod.exs           # think Rails&#39; /config/environments/production.rb
- prod.secret.exs    # think Rails&#39; /config/secrets.yml
- test.exs           # think Rails&#39; /config/environments/test.rb
/deps                # where mix deps.get put dependencies
/lib
  /pxblog
    - endpoint.ex    # a bit like config.ru and application.rb
    - repo.ex        # setup for Ecto
  - pxblog.ex        # where you can set up the OTP apps supervision tree 
/node_modules        # Phoenix integrates with Node.js
/priv
  /repo
    /migrations      # think Rails&#39; /db/migrate
    - seeds.exs      # think Rails&#39; /db/seeds.rb
  /static            # think Rails&#39; /public
    /css
    /images
    /js
/test                # think Rails&#39; /test
  /channels
  /controllers
  /models
  /support
  /views
  - test_helper.exs  # think Rails&#39; /test/test_helper.rb
/web                 # think Rails&#39; /app
  /channels          # think Rails 5&#39;s ActionCable channels
  /controllers
  /models
  /static            # think Rails&#39; /app/assets
    /assets
      /images
      /css
      /js
      /vendor
  /templates         # think Rails&#39; /app/views
    /layout
  /views             # think Rails&#39; /app/helpers but with Presenters
    - layout_view.ex
  - router.ex        # think Rails&#39; /config/routes.rb
  - web.ex           # macros to configure each MVC component
- .gitignore
- README.md
- brunch-config.js  # front-end dev reloading is controlled with Brunch
- mix.exs           # think Ruby&#39;s Gemfile (with extras)
- package.json      # Node.js dependencies</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In Rails, the starting point is the <tt>config.ru</tt> Rackup application (as Rails has been a Rack app since 3.0). It then loads the <tt>config/environment.rb</tt>, then <tt>config/application.rb</tt>, then <tt>config/boot.rb</tt> which loads the gems declared in the <tt>Gemfile</tt>, together with <tt>config/initializers/*.rb</tt> and each file in <tt>config/environments</tt>, where we set up the Rails::Application and plug in configuration and the pipeline of Rack Middlewares.</p>
<p>In Phoenix, the starting point - as in any Elixir app &ndash; is the <tt>mix.exs</tt> file. In this case it points to the &ldquo;Pxblog&rdquo; OTP/Phoenix app defined in <tt>lib/pxblog/pxblog.ex</tt>. In turn it starts up and supervises the &ldquo;Pxblog.Endpoint&rdquo; and &ldquo;Pxblog.Repo&rdquo; apps, which are defined in <tt>lib/pxblog/endpoint.ex</tt> and <tt>lib/pxblog/repo.ex</tt>, respectively.</p>
<p>If you build other OTP apps, this is where you can add to the OTP Supervisor Tree. I could say that it&rsquo;s something akin to a Rails Engine, although technically it is not the same thing, but this metaphor will do.</p>
<p>One main aspect of Rails is how it divides development, test, and production configuration in different files. Phoenix has the same thing at <tt>dev.exs</tt>, <tt>test.exs</tt>, and <tt>prod.exs</tt>. This is actually a <a href="http://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html#environments"target="_blank" rel="noopener">Mix feature</a>. Mix is a more accomplished version of Rake, and it makes sense, as José Valim also tried to push Thor to replace Rake, although it never picked up steam in the Ruby community (Rake being so ingrained everywhere). Web frameworks that don&rsquo;t enforce separation of environments by default, in this day and age, are useless. The cool thing is that every Elixir app generated through Mix gets this same useful feature.</p>
<p>Instead of having a <tt>database.yml</tt>, the database configuration is spread through the environment configuration files and the production settings are in a separate <tt>prod.secret.exs</tt> file, which is obviously ignored in <tt>.gitignore</tt>, like Rails&rsquo; <tt>secrets.yml</tt> file.</p>
<h3>MVC structure<span class="hx:absolute hx:-mt-20" id="mvc-structure"></span>
    <a href="#mvc-structure" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>You will see that each element of the MVC app in Phoenix starts like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/controllers/page_controller.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">PageController</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:controller</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="c1"># web/models/post.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Post</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:model</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="c1"># web/views/post_view.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">PostView</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:view</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="c1"># web/router.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Router</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:router</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This <tt>Pxblog.Web</tt> module is defined in the <tt>web/web.ex</tt> like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/web.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Web</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">model</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">quote</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">use</span> <span class="no">Ecto</span><span class="o">.</span><span class="n">Model</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">import</span> <span class="no">Ecto</span><span class="o">.</span><span class="n">Changeset</span>
</span></span><span class="line"><span class="cl">      <span class="n">import</span> <span class="no">Ecto</span><span class="o">.</span><span class="n">Query</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">from</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="ss">from</span><span class="p">:</span> <span class="mi">2</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">controller</span> <span class="k">do</span> <span class="o">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">view</span> <span class="k">do</span> <span class="o">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">router</span> <span class="k">do</span> <span class="o">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">channel</span> <span class="k">do</span> <span class="o">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="vi">@doc</span> <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">  When used, dispatch to the appropriate controller/view/etc.
</span></span></span><span class="line"><span class="cl"><span class="s2">  &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">defmacro</span> <span class="n">__using__</span><span class="p">(</span><span class="n">which</span><span class="p">)</span> <span class="k">when</span> <span class="n">is_atom</span><span class="p">(</span><span class="n">which</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">apply</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="n">which</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If you haven&rsquo;t yet, this is a good time to learn about <a href="http://elixir-lang.org/getting-started/meta/macros.html"target="_blank" rel="noopener">Elixir Macros</a>. Think of the code in the <tt>quote</tt> block as being &ldquo;injected&rdquo; in each module that calls <tt>use Pxblog.Web</tt>. When you <tt>use</tt> a module it calls the <tt><strong>using</strong></tt> macro. Think of it like a Ruby Module Mixin calling the <tt>included</tt> callback and executing a <tt>class_eval</tt>. As there is no concept of Classes and subclasses, we include mixins in each module to acquire the desired behaviours.</p>
<h3>MVC Router<span class="hx:absolute hx:-mt-20" id="mvc-router"></span>
    <a href="#mvc-router" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Unlike Rails&rsquo; <tt>config/routes.rb</tt>, which defines routes, the <tt>web/router.ex</tt> defines not only the routes themselves but also transformation pipelines and routing strategies:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/router.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Router</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:router</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">pipeline</span> <span class="ss">:browser</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:accepts</span><span class="p">,</span> <span class="o">[</span><span class="s2">&#34;html&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:fetch_session</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:fetch_flash</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:protect_from_forgery</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:put_secure_browser_headers</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">pipeline</span> <span class="ss">:api</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:accepts</span><span class="p">,</span> <span class="o">[</span><span class="s2">&#34;json&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">scope</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span> <span class="no">Pxblog</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">pipe_through</span> <span class="ss">:browser</span> <span class="c1"># Use the default browser stack</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span> <span class="no">PageController</span><span class="p">,</span> <span class="ss">:index</span>
</span></span><span class="line"><span class="cl">    <span class="n">resources</span> <span class="s2">&#34;/users&#34;</span><span class="p">,</span> <span class="no">UserController</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">resources</span> <span class="s2">&#34;/posts&#34;</span><span class="p">,</span> <span class="no">PostController</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">resources</span> <span class="s2">&#34;/sessions&#34;</span><span class="p">,</span> <span class="no">SessionController</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">:delete</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Other scopes may use custom stacks.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># scope &#34;/api&#34;, Pxblog do</span>
</span></span><span class="line"><span class="cl">  <span class="c1">#   pipe_through :api</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You have to read it like this:</p>
<p>The <tt>pipeline</tt> block plugs &ldquo;filters&rdquo;. They are similar to Rack middlewares that we define in <tt>config/application.rb</tt> in Rails. This is very clever because one key difference between vanilla Ruby on Rails and the <a href="https://github.com/rails-api/rails-api"target="_blank" rel="noopener">Rails-API project</a> is the removal of unneeded Rack middlewares that API endpoints don&rsquo;t need.</p>
<p>In Phoenix we can define one pipeline for web browsers and another for API clients, and you can see that the difference is the removal of Plugs.</p>
<p>Then we define the scopes based on the root paths. There is one scope &ldquo;/&rdquo; which connects to the <tt>:browser</tt> pipeline and an optional (commented out) scope &ldquo;/api&rdquo; which pipes through the <tt>:api</tt> pipeline.</p>
<p>Inside the scope block, it&rsquo;s very similar to the RESTful DSL used to define routes, which again is a good adaptation from Rails Routes. <a href="http://www.phoenixframework.org/docs/routing"target="_blank" rel="noopener">Read the documentation</a> to know the details.</p>
<p>Also similar to Rails Routes, it generates proper URL helpers that become available in Controllers, Views and Templates. Let&rsquo;s start by seeing some Rails URL helpers in code:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- app/views/posts/edit.html.erb --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= link_to &#39;Show&#39;, [@user, post] %&gt;
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= link_to &#39;Edit&#39;, edit_user_post_path(@user, post) %&gt;
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= link_to &#39;Destroy&#39;, [@user, post], method: :delete, data: { confirm: &#39;Are you sure?&#39; } %&gt;</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And in Phoenix:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- web/templates/post/edit.html.eex --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= link &#34;Show&#34;, to: user_post_path(@conn, :show, @user, post) %&gt;
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= link &#34;Edit&#34;, to: user_post_path(@conn, :edit, @user, post) %&gt;
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= link &#34;Delete&#34;, to: user_post_path(@conn, :delete, @user, post), method: :delete, data: [confirm: &#34;Are you sure?&#34;] %&gt;</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The main URL helper in Rails is able to take an Array such as <tt>[@user, post]</tt> and execute it the same way as if we wrote <tt>user_post_path(@user, post)</tt>. One difference from Rails is that instead of creating one helper for each HTTP verb you have just one helper per resource that accepts an extra parameter to indicate the verb. So we have <tt>post_path(@conn, :edit, post)</tt> instead of the Rails way of <tt>edit_post_path(post)</tt>.</p>
<p>As with Rails middlewares, a pipeline receives the request connection and pipes it through, transforming its metadata so it is cleaned up and usable within our controllers.</p>
<h3>MVC Controller<span class="hx:absolute hx:-mt-20" id="mvc-controller"></span>
    <a href="#mvc-controller" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In Phoenix we start a controller like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/controllers/post_controller.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">PostController</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:controller</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Post</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="ss">:scrub_params</span><span class="p">,</span> <span class="s2">&#34;post&#34;</span> <span class="k">when</span> <span class="n">action</span> <span class="k">in</span> <span class="o">[</span><span class="ss">:create</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="ss">:assign_user</span>
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="ss">:authorize_user</span> <span class="k">when</span> <span class="n">action</span> <span class="k">in</span> <span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">:update</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:delete</span><span class="o">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s similar to this Rails controller setup:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/controllers/posts_controller.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PostsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:assign_user</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:authorize_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">:update</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:destroy</span><span class="o">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As you may have concluded, a <tt>plug</tt> call works a bit like a <tt>before_action</tt> pipeline. The <tt>scrub_params</tt> I believe is similar to Rails&rsquo; <tt>ActionDispatch::ParamParser</tt>, but I&rsquo;m not sure; I know it clears out empty string values to nils so you don&rsquo;t update your models unnecessarily.</p>
<p>But unlike Rails, where a call to <tt>redirect_to</tt> halts the pipeline, we need to explicitly halt the pipeline like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">assign_user</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="sx">%{&#34;user_id&#34; =&gt; user_id}</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">params</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="n">user</span> <span class="o">=</span> <span class="no">Repo</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="no">Pxblog</span><span class="o">.</span><span class="n">User</span><span class="p">,</span> <span class="n">user_id</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assign</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="ss">:user</span><span class="p">,</span> <span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">put_flash</span><span class="p">(</span><span class="ss">:error</span><span class="p">,</span> <span class="s2">&#34;Invalid user!&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">redirect</span><span class="p">(</span><span class="ss">to</span><span class="p">:</span> <span class="n">page_path</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="ss">:index</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">halt</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In Phoenix, everything revolves around a request connection transformation pipeline that you can start configuring in the Router Plugs, Controller Plugs and Controller actions. All of them receive the connection from the previous step and return a transformed connection to the next step until it becomes a proper HTTP response. Unlike Rails, this path is much more explicit and you know that you will get this connection and you should pipe transformations through it until you render the final HTML or send back some error header.</p>
<p>To see how explicit, let&rsquo;s start with a normal Rails controller action:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@user</span><span class="o">.</span><span class="n">destroy</span>
</span></span><span class="line"><span class="cl">  <span class="n">respond_to</span> <span class="k">do</span> <span class="o">|</span><span class="nb">format</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="nb">format</span><span class="o">.</span><span class="n">html</span> <span class="p">{</span> <span class="n">redirect_to</span> <span class="n">users_url</span><span class="p">,</span> <span class="ss">notice</span><span class="p">:</span> <span class="s1">&#39;User was successfully destroyed.&#39;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nb">format</span><span class="o">.</span><span class="n">json</span> <span class="p">{</span> <span class="n">head</span> <span class="ss">:no_content</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Once upon a time, setting the flash notice message and redirecting were 2 different methods; newer versions merged them together for convenience. Rails also has the concept of Responders, which Phoenix doesn&rsquo;t have yet (#OpportunityToContribute!).</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">delete</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="sx">%{&#34;id&#34; =&gt; id}</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">user</span> <span class="o">=</span> <span class="no">Repo</span><span class="o">.</span><span class="n">get!</span><span class="p">(</span><span class="no">User</span><span class="p">,</span> <span class="nb">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">Repo</span><span class="o">.</span><span class="n">delete!</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">conn</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">put_flash</span><span class="p">(</span><span class="ss">:info</span><span class="p">,</span> <span class="s2">&#34;User deleted successfully.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">redirect</span><span class="p">(</span><span class="ss">to</span><span class="p">:</span> <span class="n">user_path</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="ss">:index</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You can see that, Responders aside, the Phoenix version is <strong>remarkably</strong> similar. And it goes like this for all RESTful actions of the controller. But more similar to the deceased Merb, each Phoenix Controller action has a proper function signature declaring the parameters it expects to receive, instead of having a global <tt>params</tt> hash that needs to go through <a href="https://github.com/rails/strong_parameters"target="_blank" rel="noopener">Strong Parameters</a>.</p>
<p>For different variations of the same parameters, you can declare multiple functions with the same name but different arguments to pattern match, like in the <tt>SessionController</tt> example:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/controllers/session_controller.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">SessionController</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:controller</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="sx">%{&#34;user&#34; =&gt; %{&#34;username&#34; =&gt; username, &#34;password&#34; =&gt; password}}</span><span class="p">)</span> <span class="k">when</span> <span class="ow">not</span> <span class="n">is_nil</span><span class="p">(</span><span class="n">username</span><span class="p">)</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">is_nil</span><span class="p">(</span><span class="n">password</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">Repo</span><span class="o">.</span><span class="n">get_by</span><span class="p">(</span><span class="no">User</span><span class="p">,</span> <span class="ss">username</span><span class="p">:</span> <span class="n">username</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">sign_in</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">password</span><span class="p">,</span> <span class="n">conn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">failed_login</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Here we have the same <tt>create/2</tt> function with pattern matching and guards. The second version receives anything, in case the first version fails to pattern match against the incoming parameters. Phoenix expects roughly the same structure of parameters as Rails, so it&rsquo;s very intuitive to follow.</p>
<h3>MVC Models<span class="hx:absolute hx:-mt-20" id="mvc-models"></span>
    <a href="#mvc-models" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Instead of the good old ActiveRecord (ActiveModel), in Phoenix we have to deal with <a href="http://www.phoenixframework.org/docs/ecto-models"target="_blank" rel="noopener">Ecto Models</a>. It already supports PostgreSQL, MySQL, SQLite3, MongoDB, so you&rsquo;re good to go for 99% of cases.</p>
<p>Ecto separates Model Logic from Model Persistence Management. Instead of using the Active Record design pattern, it favors the Data Mapper pattern. This is an old discussion among Railers. Many people dislike that persistence logic is kept together with business logic, and the heavy metaprogramming magic that makes ActiveRecord both very easy to get started with and very difficult to properly master.</p>
<p>You should read José Valim&rsquo;s post about <a href="http://blog.plataformatec.com.br/2015/08/working-with-ecto-associations-and-embeds/"target="_blank" rel="noopener">Ecto Associations</a> to get started. But for now, let&rsquo;s compare simple Rails and Ecto models:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/models/user.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_secure_password</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:posts</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:username</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In Rails we have the <a href="http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html"target="_blank" rel="noopener"><tt>ActiveSupport#has_secure_password</tt></a> which uses BCrypt underneath to generate a proper password digest. If you&rsquo;re building authentication from scratch you <strong>must</strong> use this construct.</p>
<p>Phoenix does not have the same feature yet (#OpportunityToContribute!) so its version is a bit more verbose to account for the BCrypt digest logic using the <a href="https://github.com/elixircnx/comeonin"target="_blank" rel="noopener">Comeonin</a> password hashing library. Let&rsquo;s go on in small steps:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/models/user.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">User</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:model</span>
</span></span><span class="line"><span class="cl">  <span class="n">import</span> <span class="no">Comeonin</span><span class="o">.</span><span class="n">Bcrypt</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">hashpwsalt</span><span class="p">:</span> <span class="mi">1</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">schema</span> <span class="s2">&#34;users&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:username</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:password_digest</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">timestamps</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Virtual Fields</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:password</span><span class="p">,</span> <span class="ss">:string</span><span class="p">,</span> <span class="ss">virtual</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:password_confirmation</span><span class="p">,</span> <span class="ss">:string</span><span class="p">,</span> <span class="ss">virtual</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">has_many</span> <span class="ss">:posts</span><span class="p">,</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Post</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The first part of Ecto Models declares the database fields, virtual fields and associations. Rails ActiveRecord prefers the approach of asking the database to send the table metadata and uses metaprogramming to create all the fields later at runtime. Many people dislike this approach and this is the alternative: explicit declaration.</p>
<p>We are mapping the <tt>User</tt> module with the <tt>&lsquo;users&rsquo;</tt> database table in the <tt>schema &ldquo;users&rdquo; do</tt> statement instead of resorting to pluralization conventions.</p>
<p>The last line has our well-known <tt>has_many</tt> association.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">User</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@required_fields</span> <span class="o">~</span><span class="n">w</span><span class="p">(</span><span class="n">username</span> <span class="n">email</span> <span class="n">password</span> <span class="n">password_confirmation</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@optional_fields</span> <span class="o">~</span><span class="n">w</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Here we have declared required fields. This is just a variable with a list of fields, not the validations themselves. This will be used in the next step to accomplish something similar to <tt>validates :username, presence: true</tt>.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">User</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">changeset</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">params</span> <span class="p">\\</span> <span class="ss">:empty</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">model</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">(</span><span class="n">params</span><span class="p">,</span> <span class="vi">@required_fields</span><span class="p">,</span> <span class="vi">@optional_fields</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">hash_password</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Instead of doing something like <tt>Post.create(params)</tt> we first create a changeset and then pass it to the Ecto main Repository. The Repository is then responsible for the persistence part. The Ecto Model is responsible for validating and cleaning up the changeset that the Repository receives.</p>
<p>The <tt>changeset/2</tt> returns an <a href="http://elixir-lang.org/getting-started/structs.html"target="_blank" rel="noopener">Elixir Struct</a> for us to work with before passing it to the Repository application for persistence.</p>
<p>In this function we can declare a pipeline of validations, constraints and other attribute transformations. For example, we plug in a <tt>hash_password/2</tt> function that will get the value in <tt>password</tt> and use Comeonin.<tt>hashpwsalt/1</tt> to transform the password string into a bcrypt digest and store it in the password_digest attribute:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">User</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">hash_password</span><span class="p">(</span><span class="n">changeset</span><span class="p">)</span> <span class="k">do</span> <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">password</span> <span class="o">=</span> <span class="n">get_change</span><span class="p">(</span><span class="n">changeset</span><span class="p">,</span> <span class="ss">:password</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">changeset</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">put_change</span><span class="p">(</span><span class="ss">:password_digest</span><span class="p">,</span> <span class="n">hashpwsalt</span><span class="p">(</span><span class="n">password</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">changeset</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And we return the transformed changeset so the pipeline can pick it up and pass it to other plugs, such as validations. If we wanted to add more validations we could do it like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">User</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">changeset</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">params</span> <span class="p">\\</span> <span class="ss">:empty</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">model</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">(</span><span class="n">params</span><span class="p">,</span> <span class="vi">@required_fields</span><span class="p">,</span> <span class="vi">@optional_fields</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">validate_length</span><span class="p">(</span><span class="ss">:password</span><span class="p">,</span> <span class="ss">min</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="ss">max</span><span class="p">:</span> <span class="mi">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">validate_length</span><span class="p">(</span><span class="ss">:username</span><span class="p">,</span> <span class="ss">min</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="ss">max</span><span class="p">:</span> <span class="mi">50</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">validate_confirmation</span><span class="p">(</span><span class="ss">:password</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">unique_constraint</span><span class="p">(</span><span class="ss">:username</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">hash_password</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>There you go. And in the controller, the <tt>update/2</tt> function, for example, will use the changeset like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="sx">%{&#34;id&#34; =&gt; id, &#34;user&#34; =&gt; user_params}</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">user</span> <span class="o">=</span> <span class="no">Repo</span><span class="o">.</span><span class="n">get!</span><span class="p">(</span><span class="no">User</span><span class="p">,</span> <span class="nb">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">changeset</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">changeset</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">user_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="no">Repo</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">changeset</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In the second line we use the Repository to query the &lsquo;users&rsquo; schema as declared in the <tt>User</tt> model.</p>
<p>Then, we transform the <tt>user</tt> struct with the <tt>user_params</tt> map that we received from the Router pipeline, as defined in the first line.</p>
<p>The transformation returns a changeset, which will contain error messages. Then we pass the changeset to the Repository again so it updates the record in the table.</p>
<h3>MVC View and Templates<span class="hx:absolute hx:-mt-20" id="mvc-view-and-templates"></span>
    <a href="#mvc-view-and-templates" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In the case of the <tt>edit/2</tt> function we call the <tt>render/3</tt> function like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/views/user_view.ex</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">edit</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="sx">%{&#34;id&#34; =&gt; id}</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">user</span> <span class="o">=</span> <span class="no">Repo</span><span class="o">.</span><span class="n">get!</span><span class="p">(</span><span class="no">User</span><span class="p">,</span> <span class="nb">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">changeset</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">changeset</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">render</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="s2">&#34;edit.html&#34;</span><span class="p">,</span> <span class="ss">user</span><span class="p">:</span> <span class="n">user</span><span class="p">,</span> <span class="ss">changeset</span><span class="p">:</span> <span class="n">changeset</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This first calls the <tt>web/views/user.ex</tt> which imports stuff like helpers, transforms the <tt>user</tt> and <tt>changeset</tt> variables into <a href="http://elixir-lang.org/getting-started/module-attributes.html"target="_blank" rel="noopener">module attributes</a> (the ones starting with &ldquo;@&rdquo; if you&rsquo;ve been wondering what those are). And the View knows to find the <tt>edit.html</tt> template at <tt>web/templates/user/edit.html.eex</tt> because it says so in <tt>web/web.ex</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/web.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Web</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">view</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">quote</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">use</span> <span class="no">Phoenix</span><span class="o">.</span><span class="n">View</span><span class="p">,</span> <span class="ss">root</span><span class="p">:</span> <span class="s2">&#34;web/templates&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1"># Import convenience functions from controllers</span>
</span></span><span class="line"><span class="cl">      <span class="n">import</span> <span class="no">Phoenix</span><span class="o">.</span><span class="n">Controller</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">get_csrf_token</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="ss">get_flash</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="ss">view_module</span><span class="p">:</span> <span class="mi">1</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1"># Use all HTML functionality (forms, tags, etc)</span>
</span></span><span class="line"><span class="cl">      <span class="n">use</span> <span class="no">Phoenix</span><span class="o">.</span><span class="n">HTML</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">import</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Router</span><span class="o">.</span><span class="n">Helpers</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I did not copy and paste all the other macros in <tt>web/web.ex</tt> but check them out to see what models, controllers, router, channel import in each module you create.</p>
<p>In Rails we have the default ERB for &ldquo;Embedded Ruby&rdquo; and in Phoenix we have &ldquo;EEX&rdquo; for &ldquo;Embedded Elixir&rdquo;. It&rsquo;s essentially the same thing: an HTML template that accepts snippets of Elixir code enclosed between <tt>&lt;%= &hellip; %&gt;</tt>. So, the <tt>edit.html.eex</tt> template looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- app/views/users/edit.html.erb --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h2</span><span class="p">&gt;</span>Edit user<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= render &#34;form.html&#34;, changeset: @changeset,
</span></span><span class="line"><span class="cl">                        action: user_path(@conn, :update, @user) %&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= link &#39;Back&#39;, to: user_path(@conn, :show, @user) %&gt; |
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= link &#34;Back&#34;, to: user_path(@conn, :index) %&gt;</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Which is very similar to the equivalent <tt>edit.html.erb</tt> in Rails:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- web/templates/user/edit.html.eex --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>Editing User<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= render &#39;form&#39; %&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= link_to &#39;Show&#39;, @user %&gt; |
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= link_to &#39;Back&#39;, users_path %&gt;</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The Phoenix version is slightly more verbose in order to not hide as much as Rails does. One can argue whether more or less magic makes it more productive, but the Phoenix version being more explicit leaves a trail of breadcrumbs that is easier to follow, especially if you&rsquo;re just getting started. Here we have no concept of &ldquo;partials&rdquo;; every template can render any other template, we just need to pass through the necessary variables for the template to function. But instead of passing a model instance we are passing a changeset.</p>
<p>The &ldquo;form.html&rdquo; template is also very similar, let&rsquo;s check out the Phoenix version first:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- web/templates/user/edit.html.eex --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= form_for @changeset, @action, fn f -&gt; %&gt;
</span></span><span class="line"><span class="cl">  <span class="err">&lt;</span>%= if @changeset.action do %&gt;
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;alert alert-danger&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Oops, something went wrong! Please check the errors below:<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;</span>%= for {attr, message} <span class="p">&lt;</span><span class="nt">-</span> <span class="na">f</span><span class="err">.</span><span class="na">errors</span> <span class="na">do</span> <span class="err">%</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span><span class="err">&lt;</span>%= humanize(attr) %&gt; <span class="err">&lt;</span>%= message %&gt;<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">&lt;</span>% end %&gt;
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="err">&lt;</span>% end %&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;form-group&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;</span>%= label f, :username, &#34;Username&#34;, class: &#34;control-label&#34; %&gt;
</span></span><span class="line"><span class="cl">    <span class="err">&lt;</span>%= text_input f, :username, class: &#34;form-control&#34; %&gt;
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  ...</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And now the Rails ERB version:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- app/views/users/_form.html.erb --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= form_for(@user) do |f| %&gt;
</span></span><span class="line"><span class="cl">  <span class="err">&lt;</span>% if @user.errors.any? %&gt;
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;error_explanation&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">h2</span><span class="p">&gt;</span><span class="err">&lt;</span>%= pluralize(@user.errors.count, &#34;error&#34;) %&gt; prohibited this user from being saved:<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="err">&lt;</span>% @user.errors.full_messages.each do |message| %&gt;
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span><span class="err">&lt;</span>%= message %&gt;<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="err">&lt;</span>% end %&gt;
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="err">&lt;</span>% end %&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;field&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;</span>%= f.label :username %&gt;<span class="p">&lt;</span><span class="nt">br</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;</span>%= f.text_field :username %&gt;
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  ...</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Remarkably similar. There is some language-specific stuff like having <tt>label(f, :username)</tt> instead of <tt>f.label :username</tt>. Because in Elixir the parentheses are also optional and because Phoenix implements helpers that are very similar to the Rails version, like &ldquo;form_for&rdquo;, we feel very comfortable very fast.</p>
<p>Rails has a default layout at <tt>app/views/layouts/application.html.erb</tt> and Phoenix has a default layout at <tt>web/templates/layout/app.html.eex</tt>. The rest is pretty much the same.</p>
<p>The <tt>mix phoenix.gen.html</tt> creates a template structure that is similar to <tt>rails generate scaffold</tt> command.</p>
<p>What Phoenix calls &ldquo;views&rdquo; is more similar to what Rails calls &ldquo;helpers&rdquo;. We can use them similarly, for example, to access the current user session, we do like this in Phoenix <tt>web/views/layout_view.ex</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/views/layout_view.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">LayoutView</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:view</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">current_user</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">Plug</span><span class="o">.</span><span class="n">Conn</span><span class="o">.</span><span class="n">get_session</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="ss">:current_user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Which is almost the same as <tt>app/helpers/application.rb</tt> in Rails:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/helpers/application.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">ApplicationHelper</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">current_user</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span><span class="o">[</span><span class="ss">:current_user</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Finally, Phoenix&rsquo;s default scaffolding already brings in Bootstrap so it looks much nicer than the 10-year-old &ldquo;scaffold.css&rdquo; that Rails generates by default. There are many gems that override that though.</p>
<h3>Tests<span class="hx:absolute hx:-mt-20" id="tests"></span>
    <a href="#tests" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The last thing is the testing system. Rails uses Minitest, Elixir uses ExUnit. Again, the helpers are so similar that you can translate almost directly from Phoenix to Rails and vice versa.</p>
<p>As a caveat, Rails tests evolved a lot in the past decade. Even without adding extra features such as Factory Girl, the standard Fixtures still don&rsquo;t have an equivalent in Phoenix (#OpportunityToContribute!).</p>
<p>Let&rsquo;s start seeing a small model test in Phoenix:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># test/models/post_test.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">PostTest</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">ModelCase</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">Post</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="vi">@valid_attrs</span> <span class="sx">%{body: &#34;some content&#34;, title: &#34;some content&#34;}</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@invalid_attrs</span> <span class="sx">%{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;changeset with valid attributes&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">changeset</span> <span class="o">=</span> <span class="no">Post</span><span class="o">.</span><span class="n">changeset</span><span class="p">(</span><span class="o">%</span><span class="no">Post</span><span class="p">{},</span> <span class="vi">@valid_attrs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">changeset</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;changeset with invalid attributes&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">changeset</span> <span class="o">=</span> <span class="no">Post</span><span class="o">.</span><span class="n">changeset</span><span class="p">(</span><span class="o">%</span><span class="no">Post</span><span class="p">{},</span> <span class="vi">@invalid_attrs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">refute</span> <span class="n">changeset</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And the same unit tests in Rails:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># test/models/post_test.rb</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PostTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">  <span class="n">setup</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@valid_attr</span> <span class="o">=</span> <span class="p">{</span><span class="ss">body</span><span class="p">:</span> <span class="s2">&#34;some content&#34;</span><span class="p">,</span> <span class="ss">title</span><span class="p">:</span> <span class="s2">&#34;some content&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@invalid_attr</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;with valid attributes&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">post</span> <span class="o">=</span> <span class="no">Post</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@valid_attr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">post</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;with invalid attributes&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">post</span> <span class="o">=</span> <span class="no">Post</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@invalid_attr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">refute</span> <span class="n">post</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span> 
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Remarkably similar. For the test data, in Phoenix we are using Elixir&rsquo;s module attributes, in Rails we put it in the setup step to create instance variables. They are not the same thing, but the result is similar. Again, in Rails we test the model, in Phoenix we test the changeset.</p>
<p>Now let&rsquo;s see some bits of a controller test. As I said before, because Phoenix does not have a Fixtures or Factory feature in place (although the nice guys at Thoughtbot just released a Factory Girl-like library for Phoenix called <a href="https://robots.thoughtbot.com/announcing-ex-machina"target="_blank" rel="noopener">ExMachina</a>) we have to do a bit more setup:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># test/controllers/session_controller.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">SessionControllerTest</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">ConnCase</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">Pxblog</span><span class="o">.</span><span class="n">User</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">setup</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">User</span><span class="o">.</span><span class="n">changeset</span><span class="p">(</span><span class="o">%</span><span class="no">User</span><span class="p">{},</span> <span class="sx">%{username: &#34;test&#34;, password: &#34;test&#34;, password_confirmation: &#34;test&#34;, email: &#34;test@test.com&#34;}</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Repo</span><span class="o">.</span><span class="n">insert</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn</span> <span class="o">=</span> <span class="n">conn</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="ss">conn</span><span class="p">:</span> <span class="n">conn</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;shows the login form&#34;</span><span class="p">,</span> <span class="sx">%{conn: conn}</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn</span> <span class="o">=</span> <span class="n">get</span> <span class="n">conn</span><span class="p">,</span> <span class="n">session_path</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="ss">:new</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">html_response</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span> <span class="o">=~</span> <span class="s2">&#34;Login&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;creates a new user session for a valid user&#34;</span><span class="p">,</span> <span class="sx">%{conn: conn}</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn</span> <span class="o">=</span> <span class="n">post</span> <span class="n">conn</span><span class="p">,</span> <span class="n">session_path</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="ss">:create</span><span class="p">),</span> <span class="ss">user</span><span class="p">:</span> <span class="sx">%{username: &#34;test&#34;, password: &#34;test&#34;}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">get_session</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="ss">:current_user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">get_flash</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="ss">:info</span><span class="p">)</span> <span class="o">==</span> <span class="s2">&#34;Sign in successful!&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">redirected_to</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span> <span class="o">==</span> <span class="n">page_path</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="ss">:index</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The Rails controller test is getting the data from a Fixture, that&rsquo;s why the setup is shorter:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># test/controllers/session_controller.rb</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SessionsControllerTest</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">  <span class="n">setup</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:user_one</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;shows the login form&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="ss">:new</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_response</span> <span class="ss">:success</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;creates a new user session for a valid user&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">post</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">user</span><span class="p">:</span> <span class="p">{</span><span class="ss">username</span><span class="p">:</span> <span class="vi">@user</span><span class="o">.</span><span class="n">username</span><span class="p">,</span> <span class="ss">password</span><span class="p">:</span> <span class="s2">&#34;password&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">session</span><span class="o">[</span><span class="ss">:current_user</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">flash</span><span class="o">[</span><span class="ss">:notice</span><span class="o">]</span> <span class="o">==</span> <span class="s2">&#34;Sign in successful!&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">user_posts_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>But as I said before, Phoenix implements similar helpers, so it&rsquo;s very straightforward to port from Rails to Phoenix here.</p>
<p>You execute the test runner in Rails with <tt>rake test</tt> and in Phoenix with <tt>mix test</tt>. Not a lot to see here. Tests load very fast, I believe they run in parallel, and it&rsquo;s very fast, which is a good departure from when you have a really large Rails app where a test suite can take minutes to finish.</p>
<h3>Conclusions<span class="hx:absolute hx:-mt-20" id="conclusions"></span>
    <a href="#conclusions" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>If you came this far, you may be interested in following Brandon&rsquo;s tutorial and then building your own Rails copy or you can just clone from my Github repositories. <a href="https://github.com/akitaonrails/pxblog_phoenix_exercise"target="_blank" rel="noopener">This is the Phoenix exercise</a> and <a href="https://github.com/akitaonrails/pxblog_rails_exercise"target="_blank" rel="noopener">this is the Rails exercise</a>.</p>
<p>What you can conclude thus far is that Phoenix is already a very full-featured web framework. And this is not even touching on what I think is its crown achievement: its remarkable WebSockets support, recently tested and benchmarked with real machines from Digital Ocean and achieving a peak performance of <a href="http://www.akitaonrails.com/2015/10/29/phoenix-experiment-holding-2-million-websocket-clients"target="_blank" rel="noopener"><strong>2 million concurrent connections</strong></a>.</p>
<p>I think this is the real use case for Phoenix: microservices to deliver on Erlang OTP&rsquo;s promise of hyper-reliability and hyper-concurrency. For that goal it&rsquo;s already production-ready and you can use it right now. There is still much to be learned regarding proper tuning, proper monitoring, proper production environment troubleshooting, and so on, but it&rsquo;s getting there.</p>
<p>As a complete Ruby on Rails replacement, it is not on par yet. But it&rsquo;s unfair to say that, because a new framework can&rsquo;t compete with 10 years of an entire ecosystem creating all sorts of features, tools and techniques for it. Rails is a powerhouse and it will continue to be so.</p>
<p>In this contrived example we already got one small feature we still don&rsquo;t have in Phoenix: the equivalent of <tt>ActiveSupport#has_secure_password</tt>. But this is not all, we still don&rsquo;t have the equivalents for Devise, Simple Forms, Active Admin or Rails Admin, Refile, Koala, Spree, etc. But we can have those, as the ecosystem starts to fill in the blanks (#OpportunityToContribute!) in this brand new Phoenix ecosystem. And I urge people to do so, as Thoughtbot already delivered their ExMachina so those who like Factory Girl can jump right in.</p>
<p>As it stands right now, it feels like the time when Rails 1.2 had just been released, many libraries were just too young, we still didn&rsquo;t have mature tools. But the upper hand of Phoenix and Elixir is the decades-old and battle-tested Erlang core. This is something no one else has, and the best way to capitalize on it is to go beyond what tools like Node.js have accomplished: a truly <strong>enjoyable</strong> programming environment, with a language that was truly well designed and targets programmer happiness, with a really mature core to boot.</p>
<p>Programmers shouldn&rsquo;t have to fight over small stuff. Task management? Done, use Mix. Package Management? Done, use Hex. Promises? Don&rsquo;t need them. Require semantics? Don&rsquo;t need them. You get the gist.</p>
<p>If you&rsquo;re a Rails programmer and you want to find a companion platform to augment your existing Rails solutions with highly reliable and highly concurrent microservices, Phoenix is an option ready for production right now. Jump right into it!</p>
]]></content:encoded><category>elixir-and-erlang</category><category>rails</category><category>web-development</category></item></channel></rss>