<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <atom:link href="https://www.mitchellhanberg.com/feed.xml" rel="self" type="application/rss+xml" />
  <title>Mitchell Hanberg&#39;s Blog</title>
  <link>https://www.mitchellhanberg.com</link>
  <description>Mitchell Hanberg&#39;s Blog</description>
  <language>en-us</language>
  <generator>Tableau v0.27.0</generator>
    <item>
       <title>Using Agenix with devShells</title>
       <link>https://www.mitchellhanberg.com/using-agenix-with-devshells/</link>
       <pubDate>Mon, 16 Jun 2025 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/using-agenix-with-devshells/</guid>
       <description><![CDATA[ <div class="markdown-alert markdown-alert-tip">
<p class="markdown-alert-title">Tip</p>
<p>This article is part of the Agenix series</p>
<ul>
<li><a href="/getting-started-with-agenix">Getting Started with Agenix</a></li>
<li><a href="/using-agenix-with-home-manager">Using Agenix with Home Manager</a></li>
<li>👉 <strong>Using Agenix with devShells</strong></li>
</ul>
</div>
<p><a href="https://nixos.org">Nix</a> with <a href="https://zero-to-nix.com/concepts/flakes/">Flakes</a> allows you to easily centralize project environments and setup right in your repository. Often, your README will tell the reader "get an API key for FOOSERVICE from a teammate"</p>
<p>Just like NixOS modules and <a href="https://github.com/nix-community/home-manager">home-manager</a>, we can use <a href="https://github.com/ryantm/agenix">Agenix</a> to encrypt and store secrets without exposing them to our git repository.</p>
<h2><a href="#flake-parts" aria-hidden="true" class="anchor" id="flake-parts"></a>flake-parts</h2>
<p>The easiest way to use Agenix with devShells is to use the <a href="https://flake.parts/options/agenix-shell">agenix-shell</a> module for <a href="https://flake.parts/index.html">flake-parts</a>.</p>
<p>Let's start with a basic flake using flake-parts.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">&lbrace;
</div><div class="line" data-line="2">  description = &quot;Agenix example for devShells&quot;;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  inputs = &lbrace;
</div><div class="line" data-line="5">    nixpkgs.url = &quot;github:nixos/nixpkgs?ref=nixos-unstable&quot;;
</div><div class="line" data-line="6"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   flake-parts.url = &quot;github:hercules-ci/flake-parts&quot;;</span>
</div><div class="line" data-line="7">  &rbrace;;
</div><div class="line" data-line="8">
</div><div class="line" data-line="9"><span style="color: #ffc0b9;"><span style="color: #8cf8f7;">-</span> outputs = &lbrace; self, nixpkgs &rbrace;:</span>
</div><div class="line" data-line="10"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span> outputs = &lbrace; self, nixpkgs, flake-parts &rbrace;: </span>
</div><div class="line" data-line="11"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   flake-parts.lib.mkFlake &lbrace;inherit inputs;&rbrace; &lbrace;</span>
</div><div class="line" data-line="12"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     imports = [];</span>
</div><div class="line" data-line="13"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     systems = [&quot;x86_64-linux&quot; &quot;aarch64-linux&quot; &quot;aarch64-darwin&quot; &quot;x86_64-darwin&quot;];</span>
</div><div class="line" data-line="14"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     perSystem = &lbrace; pkgs, ... &rbrace;: &lbrace;</span>
</div><div class="line" data-line="15"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       devShells.default = pkgs.mkShell &lbrace;</span>
</div><div class="line" data-line="16"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         packages = [];</span>
</div><div class="line" data-line="17"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       &rbrace;;</span>
</div><div class="line" data-line="18"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     &rbrace;;</span>
</div><div class="line" data-line="19"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   &rbrace;</span>
</div><div class="line" data-line="20"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span> &rbrace;;</span>
</div><div class="line" data-line="21">&rbrace;
</div></code></pre>
<h2><a href="#add-agenix-and-agenix-shell" aria-hidden="true" class="anchor" id="add-agenix-and-agenix-shell"></a>Add Agenix and agenix-shell</h2>
<p>Lets add flake-parts and the agenix-shell module.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">&lbrace;
</div><div class="line" data-line="2">  description = &quot;Agenix example for devShells&quot;;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  inputs = &lbrace;
</div><div class="line" data-line="5">    nixpkgs.url = &quot;github:nixos/nixpkgs?ref=nixos-unstable&quot;;
</div><div class="line" data-line="6">    flake-parts.url = &quot;github:hercules-ci/flake-parts&quot;;
</div><div class="line" data-line="7"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   agenix.url = &quot;github:ryantm/agenix&quot;;</span>
</div><div class="line" data-line="8"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   agenix-shell.url = &quot;github:aciceri/agenix-shell&quot;;</span>
</div><div class="line" data-line="9">  &rbrace;;
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #ffc0b9;"><span style="color: #8cf8f7;">-</span> outputs = &lbrace; self, nixpkgs, flake-parts &rbrace;:</span>
</div><div class="line" data-line="12"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span> outputs = &lbrace; self, nixpkgs, flake-parts, agenix, agenix-shell &rbrace;: </span>
</div><div class="line" data-line="13">    flake-parts.lib.mkFlake &lbrace;inherit inputs;&rbrace; &lbrace;
</div><div class="line" data-line="14"><span style="color: #ffc0b9;"><span style="color: #8cf8f7;">-</span>     imports = [];</span>
</div><div class="line" data-line="15"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>      imports = [</span>
</div><div class="line" data-line="16"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>        agenix-shell.flakeModules.default</span>
</div><div class="line" data-line="17"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>      ];</span>
</div><div class="line" data-line="18">      systems = [&quot;x86_64-linux&quot; &quot;aarch64-linux&quot; &quot;aarch64-darwin&quot; &quot;x86_64-darwin&quot;];
</div><div class="line" data-line="19"><span style="color: #ffc0b9;"><span style="color: #8cf8f7;">-</span>      perSystem = &lbrace; pkgs, ... &rbrace;: &lbrace;</span>
</div><div class="line" data-line="20"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>      perSystem = &lbrace; pkgs, config, lib ... &rbrace;: &lbrace;</span>
</div><div class="line" data-line="21">        devShells.default = pkgs.mkShell &lbrace;
</div><div class="line" data-line="22"><span style="color: #ffc0b9;"><span style="color: #8cf8f7;">-</span>         packages = [];</span>
</div><div class="line" data-line="23"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         packages = [agenix.packages.$&lbrace;system&rbrace;.default];</span>
</div><div class="line" data-line="24"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         shellHook = &#39;&#39;</span>
</div><div class="line" data-line="25"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>           source $&lbrace;lib.getExe config.agenix-shell.installationScript&rbrace;</span>
</div><div class="line" data-line="26"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         &#39;&#39;;</span>
</div><div class="line" data-line="27">        &rbrace;;
</div><div class="line" data-line="28">      &rbrace;;
</div><div class="line" data-line="29">    &rbrace;
</div><div class="line" data-line="30">  &rbrace;;
</div><div class="line" data-line="31">&rbrace;
</div></code></pre>
<h2><a href="#configure-your-secrets-recipients" aria-hidden="true" class="anchor" id="configure-your-secrets-recipients"></a>Configure your secrets recipients</h2>
<p>You can reference the section we described <a href="/getting-started-with-agenix/#5-configure-the-secrets-recipients">last time</a>.</p>
<p>This time, we're going to encrypt a file called <code>api-keys.age</code>.</p>
<h2><a href="#create-your-secrets-files" aria-hidden="true" class="anchor" id="create-your-secrets-files"></a>Create your secrets files</h2>
<p>Create your secrets files by running <code>agenix -e &lt;key-name&gt;.age</code> in the same directory as <code>secrets.nix</code>. Fill them in with the relevant values.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;"># service-a-api-key.age</span>
</div><div class="line" data-line="2">foo
</div></code></pre>
<h2><a href="#use-your-secrets" aria-hidden="true" class="anchor" id="use-your-secrets"></a>Use your secrets</h2>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">&lbrace;
</div><div class="line" data-line="2">  description = &quot;Agenix example for devShells&quot;;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  inputs = &lbrace;
</div><div class="line" data-line="5">    nixpkgs.url = &quot;github:nixos/nixpkgs?ref=nixos-unstable&quot;;
</div><div class="line" data-line="6">    flake-parts.url = &quot;github:hercules-ci/flake-parts&quot;;
</div><div class="line" data-line="7">    agenix.url = &quot;github:ryantm/agenix&quot;;
</div><div class="line" data-line="8">    agenix-shell.url = &quot;github:aciceri/agenix-shell&quot;;
</div><div class="line" data-line="9">  &rbrace;;
</div><div class="line" data-line="10">
</div><div class="line" data-line="11">  outputs = &lbrace; self, nixpkgs, flake-parts, agenix, agenix-shell &rbrace;: 
</div><div class="line" data-line="12">    flake-parts.lib.mkFlake &lbrace;inherit inputs;&rbrace; &lbrace;
</div><div class="line" data-line="13">       imports = [
</div><div class="line" data-line="14">         agenix-shell.flakeModules.default
</div><div class="line" data-line="15">       ];
</div><div class="line" data-line="16">      systems = [&quot;x86_64-linux&quot; &quot;aarch64-linux&quot; &quot;aarch64-darwin&quot; &quot;x86_64-darwin&quot;];
</div><div class="line" data-line="17"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     agenix-shell = &lbrace;</span>
</div><div class="line" data-line="18"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       secrets = &lbrace;</span>
</div><div class="line" data-line="19"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         SERVICE_A_API_KEY.file = ./service-a-api-key.age;</span>
</div><div class="line" data-line="20"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         SERVICE_B_API_KEY.file = ./service-b-api-key.age;</span>
</div><div class="line" data-line="21"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         SERVICE_C_API_KEY.file = ./service-c-api-key.age;</span>
</div><div class="line" data-line="22"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       &rbrace;;</span>
</div><div class="line" data-line="23"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     &rbrace;;</span>
</div><div class="line" data-line="24">       perSystem = &lbrace; pkgs, config, lib ... &rbrace;: &lbrace;
</div><div class="line" data-line="25">        devShells.default = pkgs.mkShell &lbrace;
</div><div class="line" data-line="26">          packages = [agenix.packages.$&lbrace;system&rbrace;.default];
</div><div class="line" data-line="27">          shellHook = &#39;&#39;
</div><div class="line" data-line="28">            source $&lbrace;lib.getExe config.agenix-shell.installationScript&rbrace;
</div><div class="line" data-line="29">          &#39;&#39;;
</div><div class="line" data-line="30">        &rbrace;;
</div><div class="line" data-line="31">      &rbrace;;
</div><div class="line" data-line="32">    &rbrace;
</div><div class="line" data-line="33">  &rbrace;;
</div><div class="line" data-line="34">&rbrace;
</div></code></pre>
<p>Your local project secrets are now available when you enter your devShell with <code>nix develop</code>.</p>
<h2><a href="#wrapping-up" aria-hidden="true" class="anchor" id="wrapping-up"></a>Wrapping Up</h2>
<p>Agenix seemed complicated to me at first, but once I got it set up up in the three usual scenarios (NixOS, home-manager, and devShells), its simplicity came to light.</p>
<p>I hope this series helps you take advantage of Agenix as well.</p>
<h2><a href="#full-example" aria-hidden="true" class="anchor" id="full-example"></a>Full Example</h2>
<h3><a href="#flakenix" aria-hidden="true" class="anchor" id="flakenix"></a>flake.nix</h3>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-nix" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">description</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">Agenix example for devShells</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea;">inputs</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea;">nixpkgs</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">url</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">github:nixos/nixpkgs?ref=nixos-unstable</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="6">    <span style="color: #e0e2ea;">flake-parts</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">url</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">github:hercules-ci/flake-parts</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="7">    <span style="color: #e0e2ea;">agenix</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">url</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">github:ryantm/agenix</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="8">    <span style="color: #e0e2ea;">agenix-shell</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">url</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">github:aciceri/agenix-shell</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="9">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11">  <span style="color: #8cf8f7;">outputs</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">self</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">nixpkgs</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">flake-parts</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">agenix</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">agenix-shell</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">:</span> 
</div><div class="line" data-line="12">    <span style="color: #e0e2ea;">flake-parts</span><span style="color: #e0e2ea;">.</span>lib<span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">mkFlake</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea; font-weight: bold;">inherit</span> inputs<span style="color: #e0e2ea;">;</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="13">       <span style="color: #e0e2ea;">imports</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="14">         <span style="color: #e0e2ea;">agenix-shell</span><span style="color: #e0e2ea;">.</span>flakeModules<span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">default</span>
</div><div class="line" data-line="15">       <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="16">      <span style="color: #e0e2ea;">systems</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">x86_64-linux</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">aarch64-linux</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">aarch64-darwin</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">x86_64-darwin</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="17">      <span style="color: #e0e2ea;">agenix-shell</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="18">        <span style="color: #e0e2ea;">secrets</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="19">          <span style="color: #e0e2ea;">SERVICE_A_API_KEY</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">file</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">./service-a-api-key.age</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="20">          <span style="color: #e0e2ea;">SERVICE_B_API_KEY</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">file</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">./service-b-api-key.age</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="21">          <span style="color: #e0e2ea;">SERVICE_C_API_KEY</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">file</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">./service-c-api-key.age</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="22">        <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="23">      <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="24">       <span style="color: #8cf8f7;">perSystem</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">pkgs</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">config</span><span style="color: #e0e2ea;">,</span> lib <span style="color: #8cf8f7;">...</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="25">        <span style="color: #e0e2ea;">devShells</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">default</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">pkgs</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">mkShell</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="26">          <span style="color: #e0e2ea;">packages</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">agenix</span><span style="color: #e0e2ea;">.</span>packages<span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">$&lbrace;</span><span style="color: #e0e2ea;">system</span><span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">default</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="27">          <span style="color: #e0e2ea;">shellHook</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&#39;&#39;</span><span style="color: #b3f6c0;"></span>
</div><div class="line" data-line="28"><span style="color: #b3f6c0;">            <span style="color: #8cf8f7;">source</span> </span><span style="color: #8cf8f7;">$&lbrace;</span>lib<span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">getExe</span> config<span style="color: #e0e2ea;">.</span>agenix-shell<span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">installationScript</span><span style="color: #8cf8f7;">&rbrace;</span><span style="color: #b3f6c0;"></span>
</div><div class="line" data-line="29"><span style="color: #b3f6c0;">          </span><span style="color: #b3f6c0;">&#39;&#39;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="30">        <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="31">      <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="32">    <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="33">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="34"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<h3><a href="#secretsnix" aria-hidden="true" class="anchor" id="secretsnix"></a>secrets.nix</h3>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-nix" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">let</span>
</div><div class="line" data-line="2">  local <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP55ETmYHSCjtvDZ/SDoHDTblYZPD2XDmObLMQvc+9xR mitchell@nixos</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="3">  remote <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5SFrZIaTh42TWQKSXeGRhBZ5CAvJWoJov+eiaUbwxa root@nixos</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="4"><span style="color: #e0e2ea; font-weight: bold;">in</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="5">  <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">service-a-api-key.age</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">publicKeys</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">local</span> <span style="color: #e0e2ea;">remote</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span> 
</div><div class="line" data-line="6">  <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">service-b-api-key.age</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">publicKeys</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">local</span> <span style="color: #e0e2ea;">remote</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span> 
</div><div class="line" data-line="7">  <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">service-c-api-key.age</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">publicKeys</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">local</span> <span style="color: #e0e2ea;">remote</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span> 
</div><div class="line" data-line="8"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre> ]]></description>
    </item>
    <item>
       <title>Compiling C with Zig</title>
       <link>https://www.mitchellhanberg.com/compiling-c-with-zig/</link>
       <pubDate>Fri, 13 Jun 2025 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/compiling-c-with-zig/</guid>
       <description><![CDATA[ <p>I'm relatively new to systems programming, having not done any since college (go <a href="https://cs.purdue.edu">Boilermakers!</a>), and back then we weren't really taught anything about build systems.</p>
<p>I think the most we did was manually calling <code>gcc</code> or were given a Makefile that no one comprehended... or was that just me 🤔.</p>
<p>I recently read through <a href="https://www.manning.com/books/modern-c">Modern C</a> by Jens Gustedt and I'm currently reading through <a href="https://www.manning.com/books/tiny-c-projects">Tiny C Projects</a> by Dan Gookin (did you know he wrote the very first "For Dummies" book??). A little unusual for me, I'm actually trying to code the exercises, which has brought me to C build systems.</p>
<p>I was perfectly fine compiling my tiny projects with <code>clang -Wall greeter.c -o greeter</code>, but since I'm also infected with the <a href="/uses/#development">Nix mind virus</a>, I wanted to be able to package it up in a <a href="https://nixos.wiki/wiki/flakes">Nix flake</a>.</p>
<p>This lead me to a conundrum. I wasn't sure how to share the build configuration between the nix-less project and the Nix flake.</p>
<p>The correct thing to do is to learn <a href="https://cmake.org/">CMake</a> and <code>make</code>, but I'm also interested in <a href="https://ziglang.org">Zig</a> and planned on doing these tiny C projects in Zig as well, so might as well try it out.</p>
<div class="markdown-alert markdown-alert-caution">
<p class="markdown-alert-title">Caution</p>
<p>This article isn't meant to imply this is the best course of action, if that wasn't already clear by me saying I'm new to this whole thing.</p>
</div>
<h2><a href="#buildzig" aria-hidden="true" class="anchor" id="buildzig"></a>build.zig</h2>
<p>This resulting <code>build.zig</code> is more or less the generated file from <code>zig init</code>, minus all the comments and anything related to tests.</p>
<p>Really, all we need to do is add an executable to the build graph, add a C source file, and link libc.</p>
<p>Now, we can run <code>zig build run</code> to run our code in one step!</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-zig" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">const</span> <span style="color: #e0e2ea;">std</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea; font-weight: bold;">@import</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;std&quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">pub</span> <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #8cf8f7;">build</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">*</span><span style="color: #e0e2ea;">std</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">Build</span><span style="color: #e0e2ea;">)</span> <span style="color: #8cf8f7;">void</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="4">    <span style="color: #e0e2ea; font-weight: bold;">const</span> <span style="color: #e0e2ea;">target</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">standardTargetOptions</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea; font-weight: bold;">const</span> <span style="color: #e0e2ea;">optimize</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">standardOptimizeOption</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7">    <span style="color: #9b9ea4;">// the main event 👇</span>
</div><div class="line" data-line="8">    <span style="color: #e0e2ea; font-weight: bold;">const</span> <span style="color: #e0e2ea;">exe</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">addExecutable</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;chapter2&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">target</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">target</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">optimize</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">optimize</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="9">    <span style="color: #e0e2ea;">exe</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">addCSourceFile</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">file</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">src_path</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">owner</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">sub_path</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;./main.c&quot;</span> <span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="10">    <span style="color: #e0e2ea;">exe</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">linkLibC</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="11">
</div><div class="line" data-line="12">    <span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">installArtifact</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">exe</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="13">
</div><div class="line" data-line="14">    <span style="color: #e0e2ea; font-weight: bold;">const</span> <span style="color: #e0e2ea;">run_cmd</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">addRunArtifact</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">exe</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="15">    <span style="color: #e0e2ea;">run_cmd</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">step</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">dependOn</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">getInstallStep</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="16">
</div><div class="line" data-line="17">    <span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">args</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">|</span><span style="color: #e0e2ea;">args</span><span style="color: #e0e2ea;">|</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="18">        <span style="color: #e0e2ea;">run_cmd</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">addArgs</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">args</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="19">    <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="20">
</div><div class="line" data-line="21">    <span style="color: #e0e2ea; font-weight: bold;">const</span> <span style="color: #e0e2ea;">run_step</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">step</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;run&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;Run the app&quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="22">    <span style="color: #e0e2ea;">run_step</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">dependOn</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&amp;</span><span style="color: #e0e2ea;">run_cmd</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">step</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="23"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<h2><a href="#nix" aria-hidden="true" class="anchor" id="nix"></a>Nix</h2>
<p>As a bonus, we'll check out the Nix flake. With this, we can run our code in one step with <code>nix run .#chapter2</code>.</p>
<h3><a href="#flakenix" aria-hidden="true" class="anchor" id="flakenix"></a>flake.nix</h3>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-nix" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">description</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">Tiny C Projects</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea;">inputs</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea;">nixpkgs</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">url</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">github:nixos/nixpkgs?ref=nixos-unstable</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8">  <span style="color: #8cf8f7;">outputs</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">nixpkgs</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">...</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea; font-weight: bold;">let</span>
</div><div class="line" data-line="9">    <span style="color: #e0e2ea; font-weight: bold;">inherit</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">nixpkgs</span><span style="color: #e0e2ea;">)</span> lib<span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="10">    systems <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="11">      <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">aarch64-darwin</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="12">      <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">x86_64-darwin</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="13">      <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">x86_64-linux</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="14">      <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">aarch64-linux</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="15">    <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="16">    <span style="color: #8cf8f7;">perSystem</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">func</span><span style="color: #8cf8f7;">:</span> lib<span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">genAttrs</span> <span style="color: #e0e2ea;">systems</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">system</span><span style="color: #8cf8f7;">:</span> <span style="color: #8cf8f7;"><span style="color: #e0e2ea;">func</span></span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">pkgs</span> <span style="color: #e0e2ea;">=</span> nixpkgs<span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">legacyPackages</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">$&lbrace;</span><span style="color: #e0e2ea;">system</span><span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="17">  <span style="color: #e0e2ea; font-weight: bold;">in</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="18">    <span style="color: #e0e2ea;">packages</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;"><span style="color: #8cf8f7;">perSystem</span></span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">pkgs</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">...</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="19">      <span style="color: #e0e2ea;">chapter2</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">pkgs</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">callPackage</span> <span style="color: #8cf8f7;">./chapter2/package.nix</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="20">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="21">    <span style="color: #e0e2ea;">devShells</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;"><span style="color: #8cf8f7;">perSystem</span></span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">pkgs</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">...</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="22">      <span style="color: #e0e2ea;">default</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">pkgs</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">mkShell</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="23">        <span style="color: #e0e2ea;">packages</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea; font-weight: bold;">with</span> <span style="color: #e0e2ea;">pkgs</span><span style="color: #e0e2ea;">;</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="24">          <span style="color: #e0e2ea;">zig_0_14</span>
</div><div class="line" data-line="25">          <span style="color: #e0e2ea;">zls</span>
</div><div class="line" data-line="26">          <span style="color: #e0e2ea;">clang-tools</span>
</div><div class="line" data-line="27">          <span style="color: #e0e2ea;">clang</span>
</div><div class="line" data-line="28">        <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="29">      <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="30">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="31">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="32"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<h3><a href="#packagenix" aria-hidden="true" class="anchor" id="packagenix"></a>package.nix</h3>
<p>Each chapter has its own separate <code>package.nix</code> file.</p>
<p>All you have to do is include <code>zig.hook</code> in <code>nativeBuildInputs</code>. This configures all the standard phases to instead call the relevant Zig commands.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-nix" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">pkgs</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">  <span style="color: #e0e2ea;">zig</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">...</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="6"><span style="color: #e0e2ea;">pkgs</span><span style="color: #e0e2ea;">.</span>stdenv<span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">mkDerivation</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="7">  <span style="color: #e0e2ea;">name</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">chapter2</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea;">version</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">0.1.0</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="9">  <span style="color: #e0e2ea;">src</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">./.</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="10">  <span style="color: #e0e2ea;">nativeBuildInputs</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="11">    <span style="color: #e0e2ea;">zig</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">hook</span>
</div><div class="line" data-line="12">    <span style="color: #e0e2ea;">pkgs</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">zig_0_14</span>
</div><div class="line" data-line="13">  <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="14"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<h2><a href="#the-code" aria-hidden="true" class="anchor" id="the-code"></a>The code</h2>
<p>You can find my solutions on my <a href="https://github.com/mhanberg/tiny-c-projects">GitHub</a>.</p> ]]></description>
    </item>
    <item>
       <title>Using Agenix with Home Manager</title>
       <link>https://www.mitchellhanberg.com/using-agenix-with-home-manager/</link>
       <pubDate>Mon, 09 Jun 2025 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/using-agenix-with-home-manager/</guid>
       <description><![CDATA[ <div class="markdown-alert markdown-alert-tip">
<p class="markdown-alert-title">Tip</p>
<p>This article is part of the Agenix series</p>
<ul>
<li><a href="/getting-started-with-agenix">Getting Started with Agenix</a></li>
<li>👉 <strong>Using Agenix with Home Manager</strong></li>
<li><a href="/using-agenix-with-devshells">Using Agenix with devShells</a></li>
</ul>
</div>
<p><a href="https://github.com/nix-community/home-manager">Home Manager</a> (also styled as home-manager) allows you to configure your programs and configuration with Nix code, similar to NixOS modules.</p>
<p>Just like NixOS modules, we can use <a href="https://github.com/ryantm/agenix">Agenix</a> to encrypt and store secrets without exposing them to our git repository.</p>
<h2><a href="#install-home-manager" aria-hidden="true" class="anchor" id="install-home-manager"></a>Install home-manager</h2>
<p>Let's start with a basic home-manager configuration to our flake from last time (sans the NixOS configuration, for brevity).</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">&lbrace;
</div><div class="line" data-line="2">  description = &quot;Agenix example for home-manager&quot;;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  inputs = &lbrace;
</div><div class="line" data-line="5">    nixpkgs.url = &quot;github:nixos/nixpkgs?ref=nixos-unstable&quot;;
</div><div class="line" data-line="6"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   home-manager = &lbrace;</span>
</div><div class="line" data-line="7"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     url = &quot;github:nix-community/home-manager&quot;;</span>
</div><div class="line" data-line="8"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     inputs.nixpkgs.follows = &quot;nixpkgs&quot;;</span>
</div><div class="line" data-line="9"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   &rbrace;;</span>
</div><div class="line" data-line="10">    agenix.url = &quot;github:ryantm/agenix&quot;;
</div><div class="line" data-line="11">  &rbrace;;
</div><div class="line" data-line="12">
</div><div class="line" data-line="13">  outputs = &lbrace;
</div><div class="line" data-line="14">    self,
</div><div class="line" data-line="15">    nixpkgs,
</div><div class="line" data-line="16"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   home-manager</span>
</div><div class="line" data-line="17">    agenix,
</div><div class="line" data-line="18">  &rbrace;: let
</div><div class="line" data-line="19">    pkgs = nixpkgs.legacyPackages.aarch64-linux;
</div><div class="line" data-line="20">  in &lbrace;
</div><div class="line" data-line="21">    devShells.aarch64-linux.default = pkgs.mkShell &lbrace;
</div><div class="line" data-line="22">      packages = [
</div><div class="line" data-line="23">        agenix.packages.aarch64-linux.default
</div><div class="line" data-line="24">      ];
</div><div class="line" data-line="25">    &rbrace;;
</div><div class="line" data-line="26"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   homeManager.&quot;mitchell@nixos&quot; = home-manager.lib.homeManagerConfiguration &lbrace;</span>
</div><div class="line" data-line="27"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     inhert pkgs;</span>
</div><div class="line" data-line="28"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     modules = [];</span>
</div><div class="line" data-line="29"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   &rbrace;;</span>
</div><div class="line" data-line="30">  &rbrace;;
</div><div class="line" data-line="31">&rbrace;
</div></code></pre>
<h2><a href="#agenix-homemanagermodule" aria-hidden="true" class="anchor" id="agenix-homemanagermodule"></a>Agenix homeManagerModule</h2>
<p>The Agenix module is responsible for decrypting and installing your secrets at runtime.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">&lbrace;
</div><div class="line" data-line="2">  description = &quot;Agenix example for home-manager&quot;;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  inputs = &lbrace;
</div><div class="line" data-line="5">    nixpkgs.url = &quot;github:nixos/nixpkgs?ref=nixos-unstable&quot;;
</div><div class="line" data-line="6">    home-manager = &lbrace;
</div><div class="line" data-line="7">      url = &quot;github:nix-community/home-manager&quot;;
</div><div class="line" data-line="8">      inputs.nixpkgs.follows = &quot;nixpkgs&quot;;
</div><div class="line" data-line="9">    &rbrace;;
</div><div class="line" data-line="10">    agenix.url = &quot;github:ryantm/agenix&quot;;
</div><div class="line" data-line="11">  &rbrace;;
</div><div class="line" data-line="12"> 
</div><div class="line" data-line="13">  outputs = &lbrace;
</div><div class="line" data-line="14">    self,
</div><div class="line" data-line="15">    nixpkgs,
</div><div class="line" data-line="16">    home-manager
</div><div class="line" data-line="17">    agenix,
</div><div class="line" data-line="18">  &rbrace;: let
</div><div class="line" data-line="19">    pkgs = nixpkgs.legacyPackages.aarch64-linux;
</div><div class="line" data-line="20">  in &lbrace;
</div><div class="line" data-line="21">    devShells.aarch64-linux.default = pkgs.mkShell &lbrace;
</div><div class="line" data-line="22">      packages = [
</div><div class="line" data-line="23">        agenix.packages.aarch64-linux.default
</div><div class="line" data-line="24">      ];
</div><div class="line" data-line="25">    &rbrace;;
</div><div class="line" data-line="26">    homeManager.&quot;mitchell@nixos&quot; = home-manager.lib.homeManagerConfiguration &lbrace;
</div><div class="line" data-line="27">      inhert pkgs;
</div><div class="line" data-line="28">      modules = [
</div><div class="line" data-line="29"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       agenix.homeManagerModules.default</span>
</div><div class="line" data-line="30">      ];
</div><div class="line" data-line="31">    &rbrace;;
</div><div class="line" data-line="32">   &rbrace;;
</div><div class="line" data-line="33">&rbrace;
</div></code></pre>
<h2><a href="#configure-your-secrets-recipients" aria-hidden="true" class="anchor" id="configure-your-secrets-recipients"></a>Configure your secrets recipients</h2>
<p>You can reference the section we described <a href="/getting-started-with-agenix/#5-configure-the-secrets-recipients">last time</a>.</p>
<p>This time, we're going to encrypt a file called <code>api-keys.age</code>.</p>
<h2><a href="#create-your-secrets-file" aria-hidden="true" class="anchor" id="create-your-secrets-file"></a>Create your secrets file</h2>
<p>Create your secrets file by running <code>agenix -e api-keys.age</code> in the same directory as <code>secrets.nix</code>. Fill it with the following environment variables.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">export</span> <span style="color: #e0e2ea;">SERVICE_A_API_KEY</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">foo</span>
</div><div class="line" data-line="2"><span style="color: #e0e2ea; font-weight: bold;">export</span> <span style="color: #e0e2ea;">SERVICE_B_API_KEY</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">bar</span>
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">export</span> <span style="color: #e0e2ea;">SERVICE_C_API_KEY</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">baz</span>
</div></code></pre>
<h2><a href="#use-your-secrets" aria-hidden="true" class="anchor" id="use-your-secrets"></a>Use your secrets</h2>
<p>We want these environment variables to be available in our shell, so we're going to export them in our zsh configuration.</p>
<p>Let's start with a basic home-manager module that enables zsh.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">&lbrace;
</div><div class="line" data-line="2">  description = &quot;Agenix example for home-manager&quot;;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  inputs = &lbrace;
</div><div class="line" data-line="5">    nixpkgs.url = &quot;github:nixos/nixpkgs?ref=nixos-unstable&quot;;
</div><div class="line" data-line="6">    home-manager = &lbrace;
</div><div class="line" data-line="7">      url = &quot;github:nix-community/home-manager&quot;;
</div><div class="line" data-line="8">      inputs.nixpkgs.follows = &quot;nixpkgs&quot;;
</div><div class="line" data-line="9">    &rbrace;;
</div><div class="line" data-line="10">    agenix.url = &quot;github:ryantm/agenix&quot;;
</div><div class="line" data-line="11">  &rbrace;;
</div><div class="line" data-line="12"> 
</div><div class="line" data-line="13">  outputs = &lbrace;
</div><div class="line" data-line="14">    self,
</div><div class="line" data-line="15">    nixpkgs,
</div><div class="line" data-line="16">    home-manager
</div><div class="line" data-line="17">    agenix,
</div><div class="line" data-line="18">  &rbrace;: let
</div><div class="line" data-line="19">    pkgs = nixpkgs.legacyPackages.aarch64-linux;
</div><div class="line" data-line="20">  in &lbrace;
</div><div class="line" data-line="21">    devShells.aarch64-linux.default = pkgs.mkShell &lbrace;
</div><div class="line" data-line="22">      packages = [
</div><div class="line" data-line="23">        agenix.packages.aarch64-linux.default
</div><div class="line" data-line="24">      ];
</div><div class="line" data-line="25">    &rbrace;;
</div><div class="line" data-line="26">    homeManager.&quot;mitchell@nixos&quot; = home-manager.lib.homeManagerConfiguration &lbrace;
</div><div class="line" data-line="27">      inhert pkgs;
</div><div class="line" data-line="28">      modules = [
</div><div class="line" data-line="29">        agenix.homeManagerModules.default
</div><div class="line" data-line="30"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       (&lbrace;...&rbrace;: &lbrace;</span>
</div><div class="line" data-line="31"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         home.stateVersion = &quot;25.11&quot;;</span>
</div><div class="line" data-line="32"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         home.username = &quot;mitchell&quot;;</span>
</div><div class="line" data-line="33"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         home.homeDirectory = &quot;/home/mitchell&quot;;</span>
</div><div class="line" data-line="34"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         programs.zsh = &lbrace;</span>
</div><div class="line" data-line="35"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>           enable = true;</span>
</div><div class="line" data-line="36"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>           initContent = &#39;&#39;</span>
</div><div class="line" data-line="37"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>           &#39;&#39;;</span>
</div><div class="line" data-line="38"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         &rbrace;</span>
</div><div class="line" data-line="39"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       &rbrace;)</span>
</div><div class="line" data-line="40">      ];
</div><div class="line" data-line="41">    &rbrace;;
</div><div class="line" data-line="42">   &rbrace;;
</div><div class="line" data-line="43">&rbrace;
</div></code></pre>
<p>Now we can configure our age secret and use its path in our zsh configuration.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">&lbrace;
</div><div class="line" data-line="2">  description = &quot;Agenix example for home-manager&quot;;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  inputs = &lbrace;
</div><div class="line" data-line="5">    nixpkgs.url = &quot;github:nixos/nixpkgs?ref=nixos-unstable&quot;;
</div><div class="line" data-line="6">    home-manager = &lbrace;
</div><div class="line" data-line="7">      url = &quot;github:nix-community/home-manager&quot;;
</div><div class="line" data-line="8">      inputs.nixpkgs.follows = &quot;nixpkgs&quot;;
</div><div class="line" data-line="9">    &rbrace;;
</div><div class="line" data-line="10">    agenix.url = &quot;github:ryantm/agenix&quot;;
</div><div class="line" data-line="11">  &rbrace;;
</div><div class="line" data-line="12"> 
</div><div class="line" data-line="13">  outputs = &lbrace;
</div><div class="line" data-line="14">    self,
</div><div class="line" data-line="15">    nixpkgs,
</div><div class="line" data-line="16">    home-manager
</div><div class="line" data-line="17">    agenix,
</div><div class="line" data-line="18">  &rbrace;: let
</div><div class="line" data-line="19">    pkgs = nixpkgs.legacyPackages.aarch64-linux;
</div><div class="line" data-line="20">  in &lbrace;
</div><div class="line" data-line="21">    devShells.aarch64-linux.default = pkgs.mkShell &lbrace;
</div><div class="line" data-line="22">      packages = [
</div><div class="line" data-line="23">        agenix.packages.aarch64-linux.default
</div><div class="line" data-line="24">      ];
</div><div class="line" data-line="25">    &rbrace;;
</div><div class="line" data-line="26">    homeManager.&quot;mitchell@nixos&quot; = home-manager.lib.homeManagerConfiguration &lbrace;
</div><div class="line" data-line="27">      inhert pkgs;
</div><div class="line" data-line="28">      modules = [
</div><div class="line" data-line="29">        agenix.homeManagerModules.default
</div><div class="line" data-line="30"><span style="color: #ffc0b9;"><span style="color: #8cf8f7;">-</span>       (&lbrace;...&rbrace;: &lbrace;</span>
</div><div class="line" data-line="31"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       (&lbrace;config, ...&rbrace;: &lbrace;</span>
</div><div class="line" data-line="32"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>          age.secrets = &lbrace;</span>
</div><div class="line" data-line="33"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>            api-keys.file = ./api-keys.age;</span>
</div><div class="line" data-line="34"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>          &rbrace;;</span>
</div><div class="line" data-line="35">          programs.zsh = &lbrace;
</div><div class="line" data-line="36">            enable = true;
</div><div class="line" data-line="37">            initContent = &#39;&#39;
</div><div class="line" data-line="38"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>             eval $(cat $&lbrace;config.age.secrets.api-keys.path&rbrace;)</span>
</div><div class="line" data-line="39">            &#39;&#39;;
</div><div class="line" data-line="40">          &rbrace;
</div><div class="line" data-line="41">        &rbrace;)
</div><div class="line" data-line="42">      ];
</div><div class="line" data-line="43">    &rbrace;;
</div><div class="line" data-line="44">   &rbrace;;
</div><div class="line" data-line="45">&rbrace;
</div></code></pre>
<p>Those environment variables are now exported in your <code>.zshrc</code> file!</p>
<p>While being an unconventional use case (for API secrets like these, you would probably use them directly in a derivation for a script or a program), this this fully demonstrates how you can use Agenix with home-manager.</p>
<h2><a href="#next-time" aria-hidden="true" class="anchor" id="next-time"></a>Next Time</h2>
<p>Home Manager is a good way to store global machine secrets like this for a particular user, but doesn't allow you to handle project level secrets.</p>
<p>Next time we'll se how we can utilize Agenix with devShells.</p>
<h2><a href="#full-example" aria-hidden="true" class="anchor" id="full-example"></a>Full Example</h2>
<h3><a href="#flakenix" aria-hidden="true" class="anchor" id="flakenix"></a>flake.nix</h3>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-nix" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">description</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">Agenix example for home-manager</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea;">inputs</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea;">nixpkgs</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">url</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">github:nixos/nixpkgs?ref=nixos-unstable</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="6">    <span style="color: #e0e2ea;">home-manager</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="7">      <span style="color: #e0e2ea;">url</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">github:nix-community/home-manager</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="8">      <span style="color: #e0e2ea;">inputs</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">nixpkgs</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">follows</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">nixpkgs</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="9">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="10">    <span style="color: #e0e2ea;">agenix</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">url</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">github:ryantm/agenix</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="11">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="12"> 
</div><div class="line" data-line="13">  <span style="color: #8cf8f7;">outputs</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="14">    <span style="color: #e0e2ea;">self</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="15">    <span style="color: #e0e2ea;">nixpkgs</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="16">    <span style="color: #e0e2ea;">home-manager</span>
</div><div class="line" data-line="17">    agenix<span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="18">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea; font-weight: bold;">let</span>
</div><div class="line" data-line="19">    pkgs <span style="color: #e0e2ea;">=</span> nixpkgs<span style="color: #e0e2ea;">.</span>legacyPackages<span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">aarch64-linux</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="20">  <span style="color: #e0e2ea; font-weight: bold;">in</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="21">    <span style="color: #e0e2ea;">devShells</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">aarch64-linux</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">default</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">pkgs</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">mkShell</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="22">      <span style="color: #e0e2ea;">packages</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="23">        <span style="color: #e0e2ea;">agenix</span><span style="color: #e0e2ea;">.</span>packages<span style="color: #e0e2ea;">.</span>aarch64-linux<span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">default</span>
</div><div class="line" data-line="24">      <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="25">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="26">    <span style="color: #e0e2ea;">homeConfigurations</span><span style="color: #e0e2ea;">.</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">mitchell@nixos</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">home-manager</span><span style="color: #e0e2ea;">.</span>lib<span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">homeManagerConfiguration</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="27">      <span style="color: #e0e2ea; font-weight: bold;">inherit</span> pkgs<span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="28">      <span style="color: #e0e2ea;">modules</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="29">        <span style="color: #e0e2ea;">agenix</span><span style="color: #e0e2ea;">.</span>homeManagerModules<span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">default</span>
</div><div class="line" data-line="30">        <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">config</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">...</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="31">          <span style="color: #e0e2ea;">home</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">stateVersion</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">25.11</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="32">          <span style="color: #e0e2ea;">home</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">username</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">mitchell</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="33">          <span style="color: #e0e2ea;">home</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">homeDirectory</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">/home/mitchell</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="34">
</div><div class="line" data-line="35">          <span style="color: #e0e2ea;">age</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">secrets</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="36">            <span style="color: #e0e2ea;">api-keys</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">file</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">./api-keys.age</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="37">          <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="38">          <span style="color: #e0e2ea;">programs</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">zsh</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="39">            <span style="color: #e0e2ea;">enable</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">true</span></span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="40">            <span style="color: #e0e2ea;">initContent</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&#39;&#39;</span><span style="color: #b3f6c0;"></span>
</div><div class="line" data-line="41"><span style="color: #b3f6c0;">              eval &quot;$(cat </span><span style="color: #8cf8f7;">$&lbrace;</span>config<span style="color: #e0e2ea;">.</span>age<span style="color: #e0e2ea;">.</span>secrets<span style="color: #e0e2ea;">.</span>api-keys<span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">path</span><span style="color: #8cf8f7;">&rbrace;</span><span style="color: #b3f6c0;">)&quot;</span>
</div><div class="line" data-line="42"><span style="color: #b3f6c0;">            </span><span style="color: #b3f6c0;">&#39;&#39;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="43">          <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="44">        <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="45">      <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="46">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="47">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="48"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<h3><a href="#secretsnix" aria-hidden="true" class="anchor" id="secretsnix"></a>secrets.nix</h3>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-nix" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">let</span>
</div><div class="line" data-line="2">  local <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP55ETmYHSCjtvDZ/SDoHDTblYZPD2XDmObLMQvc+9xR mitchell@nixos</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="3">  remote <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5SFrZIaTh42TWQKSXeGRhBZ5CAvJWoJov+eiaUbwxa root@nixos</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="4"><span style="color: #e0e2ea; font-weight: bold;">in</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="5">  <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">api-keys.age</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">publicKeys</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">local</span> <span style="color: #e0e2ea;">remote</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span> 
</div><div class="line" data-line="6"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre> ]]></description>
    </item>
    <item>
       <title>Getting Started with Agenix</title>
       <link>https://www.mitchellhanberg.com/getting-started-with-agenix/</link>
       <pubDate>Mon, 02 Jun 2025 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/getting-started-with-agenix/</guid>
       <description><![CDATA[ <div class="markdown-alert markdown-alert-tip">
<p class="markdown-alert-title">Tip</p>
<p>This article is part of the Agenix series</p>
<ul>
<li>👉 <strong>Getting Started with Agenix</strong></li>
<li><a href="/using-agenix-with-home-manager">Using Agenix with Home Manager</a></li>
<li><a href="/using-agenix-with-devshells">Using Agenix with devShells</a></li>
</ul>
</div>
<p><a href="https://nixos.wiki/wiki/NixOS_modules">NixOS modules</a> make it easy to configure many services with a consistent interface (the Nix language), but configuring confidential options like passwords and API keys this way has two major problems.</p>
<ul>
<li>Secrets should not be committed to source control in plain text.</li>
<li>Values and files used in NixOS modules are copied to the <a href="https://nix.dev/manual/nix/2.28/store/">nix store</a>, which is globally readable.</li>
</ul>
<p><a href="https://github.com/ryantm/agenix">Agenix</a> is a <a href="https://nixos.org">Nix</a> package and CLI utility that allows you to encrypt files to store secrets in your repository and then be able to decrypt them at runtime, giving access to your services.</p>
<p>The key here is that these secrets are decrypted to temporary directories with limited filesystem permissions and are kept out of the nix store.</p>
<p>Let's see what it looks like to use Agenix in these contexts through a Nix flake.</p>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">Note</p>
<p>These examples show the system as "aarch64-linux" since I am using them in a virtual machine on my Apple Silicon laptop</p>
</div>
<h2><a href="#nixos-modules" aria-hidden="true" class="anchor" id="nixos-modules"></a>NixOS Modules</h2>
<p>Let's build out an example that configures <a href="https://pi-hole.net/">PiHole</a> as an OCI container whose secrets are provided by an environment file.</p>
<h3><a href="#add-it-as-an-input" aria-hidden="true" class="anchor" id="add-it-as-an-input"></a>Add it as an input</h3>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">&lbrace;
</div><div class="line" data-line="2">  description = &quot;Agenix example for NixOS&quot;;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  inputs = &lbrace;
</div><div class="line" data-line="5">    nixpkgs.url = &quot;github:nixos/nixpkgs?ref=nixos-unstable&quot;;
</div><div class="line" data-line="6"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   agenix.url = &quot;github:ryantm/agenix&quot;;</span>
</div><div class="line" data-line="7">  &rbrace;;
</div><div class="line" data-line="8">&rbrace;
</div></code></pre>
<h3><a href="#add-the-cli-tool-to-your-package-in-your-devshell" aria-hidden="true" class="anchor" id="add-the-cli-tool-to-your-package-in-your-devshell"></a>Add the CLI tool to your package in your devShell</h3>
<p>The <code>agenix</code> CLI tool is what we use to edit our secrets locally.</p>
<p>Now, you can enter your devShell with <code>nix develop</code> and have access to the <code>agenix</code> utility.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">&lbrace;
</div><div class="line" data-line="2">  description = &quot;Agenix example for NixOS&quot;;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  inputs = &lbrace;
</div><div class="line" data-line="5">    nixpkgs.url = &quot;github:nixos/nixpkgs?ref=nixos-unstable&quot;;
</div><div class="line" data-line="6">    agenix.url = &quot;github:ryantm/agenix&quot;;
</div><div class="line" data-line="7">  &rbrace;;
</div><div class="line" data-line="8">
</div><div class="line" data-line="9"><span style="color: #ffc0b9;"><span style="color: #8cf8f7;">-</span> outputs = &lbrace; self, nixpkgs &rbrace;: &lbrace;</span>
</div><div class="line" data-line="10"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span> outputs = &lbrace; self, nixpkgs, agenix &rbrace;: let</span>
</div><div class="line" data-line="11"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   pkgs = nixpkgs.legacyPackages.aarch64-linux;</span>
</div><div class="line" data-line="12"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span> in &lbrace;</span>
</div><div class="line" data-line="13"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   devShells.aarch64-linux.default = pkgs.mkShell &lbrace;</span>
</div><div class="line" data-line="14"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     packages = [</span>
</div><div class="line" data-line="15"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       agenix.packages.aarch64-linux.default</span>
</div><div class="line" data-line="16"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     ];</span>
</div><div class="line" data-line="17">    &rbrace;;
</div><div class="line" data-line="18">  &rbrace;;
</div><div class="line" data-line="19">&rbrace;
</div></code></pre>
<h3><a href="#nixos-configuration" aria-hidden="true" class="anchor" id="nixos-configuration"></a>NixOS configuration</h3>
<p>Let's add the basic NixOS configuration before adding anything Agenix specific.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">&lbrace;
</div><div class="line" data-line="2">  description = &quot;Agenix example for NixOS&quot;;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  inputs = &lbrace;
</div><div class="line" data-line="5">    nixpkgs.url = &quot;github:nixos/nixpkgs?ref=nixos-unstable&quot;;
</div><div class="line" data-line="6">    agenix.url = &quot;github:ryantm/agenix&quot;;
</div><div class="line" data-line="7">  &rbrace;;
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">  outputs = &lbrace; self, nixpkgs, agenix &rbrace;: let
</div><div class="line" data-line="10">    pkgs = nixpkgs.legacyPackages.aarch64-linux;
</div><div class="line" data-line="11">  in &lbrace;
</div><div class="line" data-line="12">    devShells.aarch64-linux.default = pkgs.mkShell &lbrace;
</div><div class="line" data-line="13">      packages = [
</div><div class="line" data-line="14">        agenix.packages.aarch64-linux.default
</div><div class="line" data-line="15">      ];
</div><div class="line" data-line="16">    &rbrace;;
</div><div class="line" data-line="17"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   nixosConfigurations.nixos = nixpkgs.lib.nixosSystem &lbrace;</span>
</div><div class="line" data-line="18"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     system = &quot;aarch64-linux&quot;;</span>
</div><div class="line" data-line="19"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     modules = [</span>
</div><div class="line" data-line="20"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       ./configuration.nix</span>
</div><div class="line" data-line="21"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>     ];</span>
</div><div class="line" data-line="22"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   &rbrace;;</span>
</div><div class="line" data-line="23">&rbrace;
</div></code></pre>
<h3><a href="#add-the-agenix-module" aria-hidden="true" class="anchor" id="add-the-agenix-module"></a>Add the Agenix module</h3>
<p>The Agenix module is responsible for decrypting and installing your secrets at runtime.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">&lbrace;
</div><div class="line" data-line="2">  description = &quot;Agenix example for NixOS&quot;;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  inputs = &lbrace;
</div><div class="line" data-line="5">    nixpkgs.url = &quot;github:nixos/nixpkgs?ref=nixos-unstable&quot;;
</div><div class="line" data-line="6">    agenix.url = &quot;github:ryantm/agenix&quot;;
</div><div class="line" data-line="7">  &rbrace;;
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">  outputs = &lbrace; self, nixpkgs, agenix &rbrace;: let
</div><div class="line" data-line="10">    pkgs = nixpkgs.legacyPackages.aarch64-linux;
</div><div class="line" data-line="11">  in &lbrace;
</div><div class="line" data-line="12">    devShells.aarch64-linux.default = pkgs.mkShell &lbrace;
</div><div class="line" data-line="13">      packages = [
</div><div class="line" data-line="14">        agenix.packages.aarch64-linux.default
</div><div class="line" data-line="15">      ];
</div><div class="line" data-line="16">    &rbrace;;
</div><div class="line" data-line="17">    nixosConfigurations.nixos = nixpkgs.lib.nixosSystem &lbrace;
</div><div class="line" data-line="18">      system = &quot;aarch64-linux&quot;;
</div><div class="line" data-line="19">      modules = [
</div><div class="line" data-line="20">        ./configuration.nix
</div><div class="line" data-line="21"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       agenix.nixosModules.default</span>
</div><div class="line" data-line="22">      ];
</div><div class="line" data-line="23">    &rbrace;;
</div><div class="line" data-line="24">&rbrace;
</div></code></pre>
<h3><a href="#configure-the-secrets-recipients" aria-hidden="true" class="anchor" id="configure-the-secrets-recipients"></a>Configure the secrets recipients</h3>
<p>Agenix controls who can decrypt which secret with a <code>secrets.nix</code> file. This file contains the public keys used to <em>encrypt</em> the data, which also determines which private keys can <em>decrypt</em> the data.</p>
<p>With Agenix, the decryption will happen with the deployed servers ssh keys that are stored in <code>/etc/ssh</code>, so here we make <code>remote</code> equal to the public key at <code>/etc/ssh/ssh_host_ed25519_key.pub</code></p>
<p>We also set a <code>local</code> key, which is your local computer's ssh public key. This one is used by the <code>agenix</code> CLI utility to encrypt and decrypt the secrets during development</p>
<div class="markdown-alert markdown-alert-important">
<p class="markdown-alert-title">Important</p>
<p>If you are collaborating with other people, you will need to either add everyone's public keys to this file, or share a public/private key pair through something like <a href="https://developer.1password.com/docs/ssh/agent/">1Password</a>.</p>
<p>If you add a new public key, you'll need to have to rekey the files using an existing public key with <code>agenix --rekey</code>.</p>
</div>
<p>We then configure the <code>pihole.age</code> file use use these two public keys.</p>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">Note</p>
<p>As you can see by the hostname at the end of each public key, these are both on the same system. That's okay, but a little confusing. If you only try to use <code>local</code>, which is stored in <code>~/.ssh/</code>, when you attempt to run <code>nixos-rebuild</code>, you'll get an error from Agenix saying it couldn't find the right identity file to decrypt</p>
</div>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-nix" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">let</span>
</div><div class="line" data-line="2">  local <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP55ETmYHSCjtvDZ/SDoHDTblYZPD2XDmObLMQvc+9xR mitchell@nixos</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="3">  remote <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5SFrZIaTh42TWQKSXeGRhBZ5CAvJWoJov+eiaUbwxa root@nixos</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="4"><span style="color: #e0e2ea; font-weight: bold;">in</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="5">  <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">pihole.age</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">publicKeys</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">local</span> <span style="color: #e0e2ea;">remote</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span> 
</div><div class="line" data-line="6"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<h3><a href="#create-your-secrets-file" aria-hidden="true" class="anchor" id="create-your-secrets-file"></a>Create your secrets file</h3>
<p>Create your secrets file by running <code>agenix -e pihole.age</code> in the same directory as <code>secrets.nix</code>. Fill it with the following environment variables.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">FTLCONF_webserver_api_password</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">password</span>
</div></code></pre>
<p>If you try to read this file without decrypting it, you'll see something like this:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">age-encryption.org/v1</span>
</div><div class="line" data-line="2"><span style="color: #8cf8f7;">-</span><span style="color: #e0e2ea;">&gt;</span> <span style="color: #8cf8f7;">ssh-ed25519</span> <span style="color: #8cf8f7;">piHZrQ</span> <span style="color: #8cf8f7;">PuyA20t9WXtsZ7EoFS2gYLOgIsDwxtf4eC3nObAReC4</span>
</div><div class="line" data-line="3"><span style="color: #8cf8f7;">QxeIZNSg8wOzdVAFWKWgFWiXsYpdNRfGLx8wUSP/qDk</span>
</div><div class="line" data-line="4"><span style="color: #8cf8f7;">-</span><span style="color: #e0e2ea;">&gt;</span> <span style="color: #8cf8f7;">ssh-ed25519</span> <span style="color: #8cf8f7;">rglX5A</span> <span style="color: #8cf8f7;">Y1yMBwhebYIL4feWALoFykp0WIWC8hsMtEVQDCgRoyo</span>
</div><div class="line" data-line="5"><span style="color: #8cf8f7;">zHbuGsYqgdGDdWjiuEjjgEift36XAEksGPAIYbsQnQc</span>
</div><div class="line" data-line="6"><span style="color: #8cf8f7;">---</span> <span style="color: #e0e2ea;">w9m5TVJois69mM1HFXbPdmd9zp4DbzYEnmctmU3zGXA</span>
</div><div class="line" data-line="7"><span style="color: #8cf8f7;">�s�ކ�ٙq���J�d��M�</span>
</div><div class="line" data-line="8"><span style="color: #8cf8f7;">����Iț����k��r��������!��H/�����M</span>&quot;ҧ2�,W`<span style="color: #8cf8f7;">fs</span>
</div></code></pre>
<h3><a href="#pass-the-secrets-path-to-a-service" aria-hidden="true" class="anchor" id="pass-the-secrets-path-to-a-service"></a>Pass the secrets path to a service</h3>
<p>Now that we have an encrypted secrets file, we can give the path to the (eventually) decrypted file to the NixOS module. Let's add our PiHole service to our configuration, then add the secrets path.</p>
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">Note</p>
<p>Here we're using an inline NixOS module, which is just a function. You can move this to its own file and import it like the <code>configuration.nix</code> module.</p>
</div>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">&lbrace;
</div><div class="line" data-line="2">  description = &quot;Agenix example for NixOS&quot;;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  inputs = &lbrace;
</div><div class="line" data-line="5">    nixpkgs.url = &quot;github:nixos/nixpkgs?ref=nixos-unstable&quot;;
</div><div class="line" data-line="6">    agenix.url = &quot;github:ryantm/agenix&quot;;
</div><div class="line" data-line="7">  &rbrace;;
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">  outputs = &lbrace;
</div><div class="line" data-line="10">    self,
</div><div class="line" data-line="11">    nixpkgs,
</div><div class="line" data-line="12">    agenix,
</div><div class="line" data-line="13">  &rbrace;: let
</div><div class="line" data-line="14">    pkgs = nixpkgs.legacyPackages.aarch64-linux;
</div><div class="line" data-line="15">  in &lbrace;
</div><div class="line" data-line="16">    devShells.aarch64-linux.default = pkgs.mkShell &lbrace;
</div><div class="line" data-line="17">      packages = [
</div><div class="line" data-line="18">        agenix.packages.aarch64-linux.default
</div><div class="line" data-line="19">      ];
</div><div class="line" data-line="20">    &rbrace;;
</div><div class="line" data-line="21">    nixosConfigurations.nixos = nixpkgs.lib.nixosSystem &lbrace;
</div><div class="line" data-line="22">      system = &quot;aarch64-linux&quot;;
</div><div class="line" data-line="23">
</div><div class="line" data-line="24">      modules = [
</div><div class="line" data-line="25">        ./configuration.nix
</div><div class="line" data-line="26">        agenix.nixosModules.default
</div><div class="line" data-line="27"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       (&lbrace;...&rbrace;: &lbrace;</span>
</div><div class="line" data-line="28"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         virtualisation.oci-containers.containers = &lbrace;</span>
</div><div class="line" data-line="29"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>           pi-hole = &lbrace;</span>
</div><div class="line" data-line="30"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>             image = &quot;pihole/pihole:latest&quot;;</span>
</div><div class="line" data-line="31"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>             volumes = [</span>
</div><div class="line" data-line="32"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>               &quot;/var/lib/pihole:/etc/pihole&quot;</span>
</div><div class="line" data-line="33"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>             ];</span>
</div><div class="line" data-line="34"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>             hostname = &quot;pihole&quot;;</span>
</div><div class="line" data-line="35"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>             ports = [</span>
</div><div class="line" data-line="36"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>               &quot;53:53/tcp&quot;</span>
</div><div class="line" data-line="37"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>               &quot;53:53/udp&quot;</span>
</div><div class="line" data-line="38"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>               &quot;3001:80/tcp&quot;</span>
</div><div class="line" data-line="39"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>               &quot;443:443/tcp&quot;</span>
</div><div class="line" data-line="40"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>             ];</span>
</div><div class="line" data-line="41"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>           &rbrace;;</span>
</div><div class="line" data-line="42"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         &rbrace;;</span>
</div><div class="line" data-line="43"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       &rbrace;)</span>
</div><div class="line" data-line="44">      ];
</div><div class="line" data-line="45">    &rbrace;;
</div><div class="line" data-line="46">  &rbrace;;
</div><div class="line" data-line="47">&rbrace;
</div></code></pre>
<p>Now we can tell the Agenix NixOS module about our encrypted files</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">&lbrace;
</div><div class="line" data-line="2">  description = &quot;Agenix example for NixOS&quot;;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  inputs = &lbrace;
</div><div class="line" data-line="5">    nixpkgs.url = &quot;github:nixos/nixpkgs?ref=nixos-unstable&quot;;
</div><div class="line" data-line="6">    agenix.url = &quot;github:ryantm/agenix&quot;;
</div><div class="line" data-line="7">  &rbrace;;
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">  outputs = &lbrace;
</div><div class="line" data-line="10">    self,
</div><div class="line" data-line="11">    nixpkgs,
</div><div class="line" data-line="12">    agenix,
</div><div class="line" data-line="13">  &rbrace;: let
</div><div class="line" data-line="14">    pkgs = nixpkgs.legacyPackages.aarch64-linux;
</div><div class="line" data-line="15">  in &lbrace;
</div><div class="line" data-line="16">    devShells.aarch64-linux.default = pkgs.mkShell &lbrace;
</div><div class="line" data-line="17">      packages = [
</div><div class="line" data-line="18">        agenix.packages.aarch64-linux.default
</div><div class="line" data-line="19">      ];
</div><div class="line" data-line="20">    &rbrace;;
</div><div class="line" data-line="21">    nixosConfigurations.nixos = nixpkgs.lib.nixosSystem &lbrace;
</div><div class="line" data-line="22">      system = &quot;aarch64-linux&quot;;
</div><div class="line" data-line="23">
</div><div class="line" data-line="24">      modules = [
</div><div class="line" data-line="25">        ./configuration.nix
</div><div class="line" data-line="26">        agenix.nixosModules.default
</div><div class="line" data-line="27"><span style="color: #ffc0b9;"><span style="color: #8cf8f7;">-</span>       (&lbrace;...&rbrace;: &lbrace;</span>
</div><div class="line" data-line="28"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>       (&lbrace;config, ...&rbrace;: &lbrace;</span>
</div><div class="line" data-line="29"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         age.secrets = &lbrace;</span>
</div><div class="line" data-line="30"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>           pihole.file = ./pihole.age;</span>
</div><div class="line" data-line="31"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>         &rbrace;;</span>
</div><div class="line" data-line="32">          virtualisation.oci-containers.containers = &lbrace;
</div><div class="line" data-line="33">            pi-hole = &lbrace;
</div><div class="line" data-line="34">              image = &quot;pihole/pihole:latest&quot;;
</div><div class="line" data-line="35">              volumes = [
</div><div class="line" data-line="36">                &quot;/var/lib/pihole:/etc/pihole&quot;
</div><div class="line" data-line="37">              ];
</div><div class="line" data-line="38"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>             environmentFiles = [</span>
</div><div class="line" data-line="39"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>               config.age.secrets.pihole.path</span>
</div><div class="line" data-line="40"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>             ];</span>
</div><div class="line" data-line="41">              hostname = &quot;pihole&quot;;
</div><div class="line" data-line="42">              ports = [
</div><div class="line" data-line="43">                &quot;53:53/tcp&quot;
</div><div class="line" data-line="44">                &quot;53:53/udp&quot;
</div><div class="line" data-line="45">                &quot;3001:80/tcp&quot;
</div><div class="line" data-line="46">                &quot;443:443/tcp&quot;
</div><div class="line" data-line="47">              ];
</div><div class="line" data-line="48">            &rbrace;;
</div><div class="line" data-line="49">          &rbrace;;
</div><div class="line" data-line="50">        &rbrace;)
</div><div class="line" data-line="51">      ];
</div><div class="line" data-line="52">    &rbrace;;
</div><div class="line" data-line="53">  &rbrace;;
</div><div class="line" data-line="54">&rbrace;
</div></code></pre>
<h2><a href="#next-time" aria-hidden="true" class="anchor" id="next-time"></a>Next Time</h2>
<p>We built a NixOS module that consumes runtime secrets through a decrypted file that we keep encrypted at rest in our project.</p>
<p>Next time we'll see how we can utilize Agenix with <a href="https://home-manager.dev/manual/23.05/index.html">home-manager</a>!</p>
<h2><a href="#full-example" aria-hidden="true" class="anchor" id="full-example"></a>Full Example</h2>
<h3><a href="#flakenix" aria-hidden="true" class="anchor" id="flakenix"></a>flake.nix</h3>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-nix" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">description</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">Agenix example for NixOS</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea;">inputs</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea;">nixpkgs</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">url</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">github:nixos/nixpkgs?ref=nixos-unstable</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="6">    <span style="color: #e0e2ea;">agenix</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">url</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">github:ryantm/agenix</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="7">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">  <span style="color: #8cf8f7;">outputs</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="10">    <span style="color: #e0e2ea;">self</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="11">    <span style="color: #e0e2ea;">nixpkgs</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="12">    <span style="color: #e0e2ea;">agenix</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="13">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea; font-weight: bold;">let</span>
</div><div class="line" data-line="14">    pkgs <span style="color: #e0e2ea;">=</span> nixpkgs<span style="color: #e0e2ea;">.</span>legacyPackages<span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">aarch64-linux</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="15">  <span style="color: #e0e2ea; font-weight: bold;">in</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="16">    <span style="color: #e0e2ea;">devShells</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">aarch64-linux</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">default</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">pkgs</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">mkShell</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="17">      <span style="color: #e0e2ea;">packages</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="18">        <span style="color: #e0e2ea;">agenix</span><span style="color: #e0e2ea;">.</span>packages<span style="color: #e0e2ea;">.</span>aarch64-linux<span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">default</span>
</div><div class="line" data-line="19">      <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="20">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="21">    <span style="color: #e0e2ea;">nixosConfigurations</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">nixos</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">nixpkgs</span><span style="color: #e0e2ea;">.</span>lib<span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">nixosSystem</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="22">      <span style="color: #e0e2ea;">system</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">aarch64-linux</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="23">
</div><div class="line" data-line="24">      <span style="color: #e0e2ea;">modules</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="25">        <span style="color: #8cf8f7;">./configuration.nix</span>
</div><div class="line" data-line="26">        <span style="color: #e0e2ea;">agenix</span><span style="color: #e0e2ea;">.</span>nixosModules<span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">default</span>
</div><div class="line" data-line="27">        <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">config</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">...</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="28">          <span style="color: #e0e2ea;">age</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">secrets</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="29">            <span style="color: #e0e2ea;">pihole</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">file</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">./pihole.age</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="30">          <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="31">          <span style="color: #e0e2ea;">virtualisation</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">oci-containers</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">containers</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="32">            <span style="color: #e0e2ea;">pi-hole</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="33">              <span style="color: #e0e2ea;">image</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">pihole/pihole:latest</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="34">              <span style="color: #e0e2ea;">volumes</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="35">                <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">/var/lib/pihole:/etc/pihole</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="36">              <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="37">              <span style="color: #e0e2ea;">environmentFiles</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="38">                <span style="color: #e0e2ea;">config</span><span style="color: #e0e2ea;">.</span>age<span style="color: #e0e2ea;">.</span>secrets<span style="color: #e0e2ea;">.</span>pihole<span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">path</span>
</div><div class="line" data-line="39">              <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="40">              <span style="color: #e0e2ea;">hostname</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">pihole</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="41">              <span style="color: #e0e2ea;">ports</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="42">                <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">53:53/tcp</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="43">                <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">53:53/udp</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="44">                <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">3001:80/tcp</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="45">                <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">443:443/tcp</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="46">              <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="47">            <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="48">          <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="49">        <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="50">      <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="51">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="52">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="53"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<h3><a href="#secretsnix" aria-hidden="true" class="anchor" id="secretsnix"></a>secrets.nix</h3>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-nix" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">let</span>
</div><div class="line" data-line="2">  local <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP55ETmYHSCjtvDZ/SDoHDTblYZPD2XDmObLMQvc+9xR mitchell@nixos</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="3">  remote <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5SFrZIaTh42TWQKSXeGRhBZ5CAvJWoJov+eiaUbwxa root@nixos</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="4"><span style="color: #e0e2ea; font-weight: bold;">in</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="5">  <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">pihole.age</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">publicKeys</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">local</span> <span style="color: #e0e2ea;">remote</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">;</span> 
</div><div class="line" data-line="6"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre> ]]></description>
    </item>
    <item>
       <title>Code BEAM America 2025</title>
       <link>https://www.mitchellhanberg.com/code-beam-america-2025/</link>
       <pubDate>Tue, 11 Mar 2025 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/code-beam-america-2025/</guid>
       <description><![CDATA[ <p>My first conference of 2025 was <a href="https://codebeamamerica.com">Code BEAM America</a> and I had the honor of attending with a great crew from DraftKings (<a href="https://careers.draftkings.com/jobs/jr10888/lead-software-engineer-elixir/">we're hiring!</a>) and to give my second ever conference talk!</p>
<h2><a href="#on-the-elixir-community" aria-hidden="true" class="anchor" id="on-the-elixir-community"></a>On the Elixir community</h2>
<p>Reflecting on this year's conference, I realized how comfortable I was the entire time. After 7 years in the Elixir community, I've accumulated a number for friends and acquaintances, something I really never expected from a work adjacent interest like a programming language.</p>
<p>At my first Elixir conference, <a href="https://web.archive.org/web/20200308234856/https://lonestarelixir.com/">Lonestar Elixir 2020</a> (rough year, but also the year I "went pro" with Elixir), I had a couple of acquaintances from my new job at <a href="https://bleacherreport.com/">Bleacher Report</a>, but I hadn't really met them yet. I'm a pretty social guy, but traveling across the country to hang out with a bunch of strangers isn't really my comfort zone.</p>
<p>Luckily, I met folks like <a href="https://supersimple.org/">Todd Resudek</a> who go out of their way to be friendly and introduce you to people they know. I've tried to embody this spirit myself at social gatherings ever since, and Code BEAM America 2025 was no exception.</p>
<h2><a href="#my-talk" aria-hidden="true" class="anchor" id="my-talk"></a>My talk</h2>
<p>This year I gave an update on our progress with the <a href="/ive-joined-the-official-elixir-lsp-team/">unified language server project</a>.</p>
<p>We are a little more than 6 months into the project and it felt great to finally share how it's going and what to expect.</p>
<p>I spoke to a number of folks on what they're excited about with the project and had a blast exploring the possibilities of the next generation of Elixir tooling.</p>
<p>You can watch my talk on <a href="https://www.youtube.com/watch?v=2vIw2fb9DxA">YouTube</a>.</p>
<h2><a href="#the-talks" aria-hidden="true" class="anchor" id="the-talks"></a>The talks</h2>
<p>I thoroughly enjoyed the talks I attended this year (I can admit I do enjoy the hallway track as well...) with a couple of notable talks.</p>
<h3><a href="#jason-axelson---choosing-an-effective-testing-structure" aria-hidden="true" class="anchor" id="jason-axelson---choosing-an-effective-testing-structure"></a>Jason Axelson - Choosing an effective testing structure</h3>
<p><a href="https://www.linkedin.com/in/jasonaxelson/">Jason Axelson</a> gave a presentation on testing practices from which I almost broke neck from aggressively nodding the entire time. <a href="https://www.youtube.com/watch?v=LutLGOxnSYE">Watch it on YouTube!</a></p>
<p>One of his suggestions I believe is the most powerful: "Make your tests async" (paraphrased, I didn't write it down because of the aforementioned head banging).</p>
<p>The rationale is basically that by making your tests async, you are forced to iron out a lot of the concurrency kinks in your programs and it helps you really understand your codebase from the bottom up. Improving your own understanding of your codebases will pay dividends when it comes time to handle the unfortunate production incident or training juniors/new teammates.</p>
<p>Later on, I shared with Jason an <a href="https://github.com/mhanberg/sandrabbit">example repository</a> I had made which demonstrates how to structure a Phoenix application that deals with singleton GenServers (global state) and still maintain async tests.</p>
<p>PS: Isn't it cool that at a conference you can watch a presentation and then later eat lunch (or drink beer) next to them and discuss it??</p>
<h3><a href="#jeffrey-matthias-and-aidan-obley---crafting-fully-custom-code-generators" aria-hidden="true" class="anchor" id="jeffrey-matthias-and-aidan-obley---crafting-fully-custom-code-generators"></a>Jeffrey Matthias and Aidan Obley - Crafting Fully Custom Code Generators</h3>
<p>In this talk, <a href="https://www.linkedin.com/in/jeffreymatthias/">Jeffrey</a> and <a href="https://www.linkedin.com/in/adobley/">Aidan</a> cover how they use custom generators at <a href="https://www.mechanical-orchard.com/">Mechanical Orchard</a> to help rebuild legacy programs in Elixir.</p>
<p>In their talk, Jeffrey notes (paraphrased) "Remember, this is <em>your</em> app, you can name the folders what you want and place the files where you'd like".</p>
<p>I think this is an important thing to realize. I love that our community and frameworks have conventions, but sometimes I feel that folks become blinded to what they're "allowed" to do.</p>
<p>The default Phoenix generators might have a certain naming scheme or directory structure, but that doesn't mean it's the ordained way to structure your program (it's mostly superficial anyway.)</p>
<h2><a href="#see-ya-next-time" aria-hidden="true" class="anchor" id="see-ya-next-time"></a>See ya next time</h2>
<p>I had a blast and I hope to catch ya at the next Code BEAM 👋</p> ]]></description>
    </item>
    <item>
       <title>I&#39;ve Joined the Official Elixir LSP Team</title>
       <link>https://www.mitchellhanberg.com/ive-joined-the-official-elixir-lsp-team/</link>
       <pubDate>Wed, 21 Aug 2024 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/ive-joined-the-official-elixir-lsp-team/</guid>
       <description><![CDATA[ <p>I've joined the official Elixir language server team!</p>
<p>This is the culmination of the efforts from <a href="https://github.com/jonatanklosko">Jonatan Kłosko</a> (of LiveBook), <a href="https://github.com/scohen">Steve Cohen</a> (of Lexical), <a href="https://github.com/lukaszsamson">Łukasz Samson</a> (of ElixirLS), and myself (<a href="https://github.com/mhanberg">Mitchell Hanberg</a> of Next LS).</p>
<p>You can read more about it on <a href="https://elixir-lang.org/blog/2024/08/15/welcome-elixir-language-server-team/">elixir-lang.org</a>.</p>
<p>We are currently working on merging the three projects (Lexical, ElixirLS, and Next LS) into a single official project, so in the meantime, you can continue to use
the language server you are currently using.</p>
<p>If you would like to support my efforts on this project, please sponsor me on GitHub! I have a goal of reaching 100 sponsors, and as of writing I'm a little more than halfway there.</p>
<p><strong>Sponsor link:</strong> <a href="https://github.com/sponsors/mhanberg">https://github.com/sponsors/mhanberg</a></p>
<p>If you sponsor and would like an elixir-tools sticker, please email me your name and address and I'll send one your way (even internationally!)</p> ]]></description>
    </item>
    <item>
       <title>Validate and Transform Your Data with Schematic</title>
       <link>https://www.mitchellhanberg.com/validate-and-transform-your-data-with-schematic/</link>
       <pubDate>Fri, 12 Jul 2024 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/validate-and-transform-your-data-with-schematic/</guid>
       <description><![CDATA[ <p>I've recently seen talk of similar libraries to <a href="https://github.com/mhanberg/schematic">Schematic</a> so I figured I'd share my take on the problem space!</p>
<h2><a href="#schematic" aria-hidden="true" class="anchor" id="schematic"></a>Schematic</h2>
<p>Schematic is a library for validating and transforming data in Elixir, similar to <a href="https://hexdocs.pm/ecto/3.11.2/Ecto.Changeset.html#field_missing?/2?utm_source=thinkingelixir&amp;utm_medium=shownotes">Ecto Changesets</a> and <a href="https://github.com/elixir-toniq/norm">Norm</a>.</p>
<p>I created Schematic early in 2023 while developing the <a href="https://github.com/elixir-tools/gen_lsp">GenLSP</a> library, which I developed to build <a href="https://www.elixir-tools.dev/docs/next-ls/quickstart/">Next LS</a>. I needed to be able to consume and produce data structures described by the Language Server Protocol JSON Schema specification, which describes data in terms of basic scalar types and certain compound types, similar to some algebraic data types.</p>
<p>I wanted a library that lends itself to expressiveness, composition, and code generation.</p>
<h2><a href="#basic-schematics" aria-hidden="true" class="anchor" id="basic-schematics"></a>Basic schematics</h2>
<p>Schematic provides... <em>schematics</em> for basic primitive types in Elixir.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Schematic</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">int</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="4"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok, 1&rbrace;</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6"><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">int</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;one&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="7"><span style="color: #9b9ea4;">#=&gt; &lbrace;:error, &quot;expected an integer&quot;&rbrace;</span>
</div></code></pre>
<p>These are just functions, so you can bind them to variables and return them from functions.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">Numbers</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Schematic</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">integer</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #8cf8f7;">int</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">validate</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">value</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">integer</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">value</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9"><span style="color: #e0e2ea;">Numbers</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">validate</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="10"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok, 1&rbrace;</span>
</div><div class="line" data-line="11">
</div><div class="line" data-line="12"><span style="color: #e0e2ea;">Numbers</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">validate</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;one&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="13"><span style="color: #9b9ea4;">#=&gt; &lbrace;:error, &quot;expected an integer&quot;&rbrace;</span>
</div></code></pre>
<h2><a href="#list-and-tuple-schematics" aria-hidden="true" class="anchor" id="list-and-tuple-schematics"></a>List and Tuple schematics</h2>
<p>We can also define schematics for lists and tuples.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">Hobbies</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Schematic</span>
</div><div class="line" data-line="3">    
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">validate</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">values</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">list</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">values</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #e0e2ea;">Hobbies</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">validate</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;foosball&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;cooking&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="8"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok, [&quot;foosball&quot;, &quot;cooking&quot;]&rbrace;</span>
</div><div class="line" data-line="9">
</div><div class="line" data-line="10"><span style="color: #e0e2ea;">Hobbies</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">validate</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;cooking&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="11"><span style="color: #9b9ea4;">#=&gt; &lbrace;:error, [error: &quot;expected a string&quot;, ok: &quot;cooking&quot;]&rbrace;</span>
</div><div class="line" data-line="12">
</div><div class="line" data-line="13"><span style="color: #e0e2ea;">Hobbies</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">validate</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;foosball&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="14"><span style="color: #9b9ea4;">#=&gt; &lbrace;:error, &quot;expected a list&quot;&rbrace;</span>
</div><div class="line" data-line="15">
</div></code></pre>
<h2><a href="#map-and-struct-schematics" aria-hidden="true" class="anchor" id="map-and-struct-schematics"></a>Map and Struct schematics</h2>
<p>Map and struct (or <code>schema</code> in Schematic parlance) schematics are versatile and extendable.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">Animals</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Schematic</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">    <span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="6">      <span style="color: #8cf8f7;">species: </span><span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">      <span style="color: #8cf8f7;">genus: </span><span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="8">      <span style="color: #8cf8f7;">color: </span><span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="9">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="10">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="11">
</div><div class="line" data-line="12">  <span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">Cat</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="13">    <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="14">      <span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Animals</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="15">        <span style="color: #8cf8f7;">breed: </span><span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="16">        <span style="color: #8cf8f7;">declawed: </span><span style="color: #8cf8f7;">bool</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="17">      <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="18">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="19">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="20"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="21">
</div><div class="line" data-line="22"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Animals</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="23"><span style="color: #9b9ea4;">#=&gt; &lbrace;:error,</span>
</div><div class="line" data-line="24"><span style="color: #9b9ea4;"># %&lbrace;</span>
</div><div class="line" data-line="25"><span style="color: #9b9ea4;">#   color: &quot;expected a string&quot;,</span>
</div><div class="line" data-line="26"><span style="color: #9b9ea4;">#   species: &quot;expected a string&quot;,</span>
</div><div class="line" data-line="27"><span style="color: #9b9ea4;">#   genus: &quot;expected a string&quot;</span>
</div><div class="line" data-line="28"><span style="color: #9b9ea4;"># &rbrace;&rbrace;</span>
</div><div class="line" data-line="29">
</div><div class="line" data-line="30"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Animals</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">color: </span><span style="color: #b3f6c0;">&quot;black&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="31"><span style="color: #9b9ea4;">#=&gt; &lbrace;:error, %&lbrace;species: &quot;expected a string&quot;, genus: &quot;expected a string&quot;&rbrace;&rbrace;</span>
</div><div class="line" data-line="32">
</div><div class="line" data-line="33"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Animals</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">color: </span><span style="color: #b3f6c0;">&quot;black&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">species: </span><span style="color: #b3f6c0;">&quot;lupus&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">genus: </span><span style="color: #b3f6c0;">&quot;canis&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="34"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok, %&lbrace;color: &quot;black&quot;, species: &quot;lupus&quot;, genus: &quot;canis&quot;&rbrace;&rbrace;</span>
</div><div class="line" data-line="35">
</div><div class="line" data-line="36"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Animals.Cat</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">color: </span><span style="color: #b3f6c0;">&quot;orange&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">species: </span><span style="color: #b3f6c0;">&quot;catus&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">genus: </span><span style="color: #b3f6c0;">&quot;felis&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="37"><span style="color: #9b9ea4;">#=&gt; &lbrace;:error, %&lbrace;breed: &quot;expected a string&quot;, declawed: &quot;expected a boolean&quot;&rbrace;&rbrace;</span>
</div><div class="line" data-line="38">
</div><div class="line" data-line="39"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Animals.Cat</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="40">  <span style="color: #8cf8f7;">color: </span><span style="color: #b3f6c0;">&quot;orange&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="41">  <span style="color: #8cf8f7;">species: </span><span style="color: #b3f6c0;">&quot;catus&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="42">  <span style="color: #8cf8f7;">genus: </span><span style="color: #b3f6c0;">&quot;felis&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="43">  <span style="color: #8cf8f7;">breed: </span><span style="color: #b3f6c0;">&quot;shorthair&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="44">  <span style="color: #8cf8f7;">declawed: </span><span style="color: #e0e2ea;">false</span>
</div><div class="line" data-line="45"><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="46"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok,</span>
</div><div class="line" data-line="47"><span style="color: #9b9ea4;"># %&lbrace;</span>
</div><div class="line" data-line="48"><span style="color: #9b9ea4;">#   color: &quot;orange&quot;,</span>
</div><div class="line" data-line="49"><span style="color: #9b9ea4;">#   species: &quot;catus&quot;,</span>
</div><div class="line" data-line="50"><span style="color: #9b9ea4;">#   genus: &quot;felis&quot;,</span>
</div><div class="line" data-line="51"><span style="color: #9b9ea4;">#   breed: &quot;shorthair&quot;,</span>
</div><div class="line" data-line="52"><span style="color: #9b9ea4;">#   declawed: false</span>
</div><div class="line" data-line="53"><span style="color: #9b9ea4;"># &rbrace;&rbrace;</span>
</div></code></pre>
<p>Structs can be created from plain maps using the <code>schema</code> schematic. By default it looks for string keys and converts them to atom keys, but that can be disabled using the <code>convert: false</code> option.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">Animals</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Schematic</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">defstruct</span> <span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">:species</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:genus</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:color</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="7">    <span style="color: #8cf8f7;">schema</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">__MODULE__</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="8">      <span style="color: #8cf8f7;">species: </span><span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="9">      <span style="color: #8cf8f7;">genus: </span><span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="10">      <span style="color: #8cf8f7;">color: </span><span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="11">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="12">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="13"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="14">
</div><div class="line" data-line="15"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Animals</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #b3f6c0;">&quot;species&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #b3f6c0;">&quot;lupus&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;genus&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #b3f6c0;">&quot;canis&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;color&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #b3f6c0;">&quot;grey&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="16"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok, %Animals&lbrace;species: &quot;lupus&quot;, genus: &quot;canis&quot;, color: &quot;grey&quot;&rbrace;&rbrace;</span>
</div></code></pre>
<h3><a href="#optional-keys-and-nullable-fields" aria-hidden="true" class="anchor" id="optional-keys-and-nullable-fields"></a>Optional keys and nullable fields</h3>
<p>Optional keys and nullable values can be specified using the <code>optional</code> and <code>nullable</code> schematics. Combining this with a struct, you can create default values for certain keys.</p>
<p><code>optional</code> keys do not have to be present, but if they are, the value must unify with the given schematic.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">Animals</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Schematic</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">defstruct</span> <span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">:species</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:genus</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">color: </span><span style="color: #b3f6c0;">&quot;brown&quot;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="7">    <span style="color: #8cf8f7;">schema</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">__MODULE__</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="8">      <span style="color: #8cf8f7;">optional</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">:color</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="9">      <span style="color: #8cf8f7;">species: </span><span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="10">      <span style="color: #8cf8f7;">genus: </span><span style="color: #8cf8f7;">nullable</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="11">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="12">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="13"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="14">
</div><div class="line" data-line="15"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Animals</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="16">  <span style="color: #b3f6c0;">&quot;species&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #b3f6c0;">&quot;lupus&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="17">  <span style="color: #b3f6c0;">&quot;genus&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">nil</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="18"><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="19"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok, %Animals&lbrace;species: &quot;lupus&quot;, genus: nil, color: &quot;brown&quot;&rbrace;&rbrace;</span>
</div></code></pre>
<h3><a href="#tranforming-keys" aria-hidden="true" class="anchor" id="tranforming-keys"></a>Tranforming keys</h3>
<p>While <code>schema</code> will automatically convert string to atom keys, you can also use a tuple for the key specification for key transformation like camelCase to snake_case.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">JobPosting</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Schematic</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">    <span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="6">      <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #b3f6c0;">&quot;startDate&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:start_date</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">      <span style="color: #8cf8f7;">optional</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #b3f6c0;">&quot;salaryRange&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:salary_range</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="8">      <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #b3f6c0;">&quot;title&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:title</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="9">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="10">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="11"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="12">
</div><div class="line" data-line="13"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">JobPosting</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="14">  <span style="color: #b3f6c0;">&quot;startDate&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #b3f6c0;">&quot;Jan 1, 2025&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="15">  <span style="color: #b3f6c0;">&quot;title&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #b3f6c0;">&quot;Chicken Tender Engineer&quot;</span>
</div><div class="line" data-line="16"><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="17"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok, %&lbrace;title: &quot;Chicken Tender Engineer&quot;, start_date: &quot;Jan 1, 2025&quot;&rbrace;&rbrace;</span>
</div></code></pre>
<p>You can also use the <code>dump</code> function to transform keys in reverse.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">dump</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">JobPosting</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">title: </span><span style="color: #b3f6c0;">&quot;Chicken Tender Engineer&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">  <span style="color: #8cf8f7;">start_date: </span><span style="color: #b3f6c0;">&quot;Jan 1, 2025&quot;</span>
</div><div class="line" data-line="4"><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="5"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok, %&lbrace;&quot;startDate&quot; =&gt; &quot;Jan 1, 2025&quot;, &quot;title&quot; =&gt; &quot;Chicken Tender Engineer&quot;&rbrace;&rbrace;</span>
</div></code></pre>
<h2><a href="#advanced-schematics" aria-hidden="true" class="anchor" id="advanced-schematics"></a>Advanced schematics</h2>
<p>Most of your data is rather complex and can be specified further than just a "string", you might have an enumeration, you might actually say a value can be either a <code>Mammal</code> or a <code>Reptile</code>, or convert an ISO timestamp into an Elixir DateTime struct.</p>
<h3><a href="#oneof" aria-hidden="true" class="anchor" id="oneof"></a><code>oneof</code></h3>
<p>If you want to say a value is "one of" a list of schematics, you can use the <code>oneof</code> schematic. I believe the semantics are similar to an enum or union type from other languages.</p>
<p>In this example, we also demonstrate using literals as schematics.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">HousePet</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Schematic</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">    <span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="6">      <span style="color: #8cf8f7;">name: </span><span style="color: #8cf8f7;">str</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">      <span style="color: #8cf8f7;">type:</span>
</div><div class="line" data-line="8"><span style="color: #8cf8f7;"></span>        <span style="color: #8cf8f7;">oneof</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="9">          <span style="color: #b3f6c0;">&quot;Dog&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="10">          <span style="color: #b3f6c0;">&quot;Cat&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="11">          <span style="color: #b3f6c0;">&quot;Hamster&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="12">          <span style="color: #b3f6c0;">&quot;Fish&quot;</span>
</div><div class="line" data-line="13">        <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="14">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="15">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="16"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>Making an enum of strings is nice, but for a proper union type style schematic, we can use other schematics, even map schematics.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">HousePet</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Schematic</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">dog</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">type: </span><span style="color: #b3f6c0;">&quot;dog&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="5">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">cat</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">type: </span><span style="color: #b3f6c0;">&quot;cat&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">hamster</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">type: </span><span style="color: #b3f6c0;">&quot;hamster&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="7">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">fish</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">type: </span><span style="color: #b3f6c0;">&quot;fish&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="10">    <span style="color: #8cf8f7;">oneof</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="11">      <span style="color: #8cf8f7;">dog</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="12">      <span style="color: #8cf8f7;">cat</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="13">      <span style="color: #8cf8f7;">hamster</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="14">      <span style="color: #8cf8f7;">fish</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="15">    <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="16">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="17"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="18">
</div><div class="line" data-line="19"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">HousePet</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">type: </span><span style="color: #b3f6c0;">&quot;cat&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="20"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok, %&lbrace;type: &quot;cat&quot;&rbrace;&rbrace;</span>
</div></code></pre>
<p>Unfortunately the error message for these kind of failure case is not very good, but will be improved in a future version Schematic.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">HousePet</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">type: </span><span style="color: #b3f6c0;">&quot;snake&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="2"><span style="color: #9b9ea4;">#=&gt; &lbrace;:error, &quot;expected either a map, a map, a map, or a map&quot;&rbrace;</span>
</div></code></pre>
<h2><a href="#value-based-validations" aria-hidden="true" class="anchor" id="value-based-validations"></a>Value based validations</h2>
<p>So far we've covered more <em>structural</em> style of data validation, but we can also do more value based validations that you are probably used to in your <code>Ecto.Changeset</code> code.</p>
<p>We can use the <code>all</code> and <code>raw</code> schematics to accomplish this!</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">SpecialNumber</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">schematic</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">all</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="4">      <span style="color: #8cf8f7;">int</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5">      <span style="color: #8cf8f7;">raw</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&amp;</span><span style="color: #e0e2ea;">Kernel</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">&lt;</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&amp;</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">10</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">message: </span><span style="color: #b3f6c0;">&quot;must be less than 10&quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="6">      <span style="color: #8cf8f7;">raw</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&amp;</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Kernel</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">rem</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&amp;</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">2</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">==</span> <span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">message: </span><span style="color: #b3f6c0;">&quot;must be divisible by 2&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="7">    <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">SpecialNumber</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">8</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="12"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok, 8&rbrace;</span>
</div><div class="line" data-line="13">
</div><div class="line" data-line="14"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">SpecialNumber</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">15</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="15"><span style="color: #9b9ea4;">#=&gt; &lbrace;:error, [&quot;must be less than 10&quot;, &quot;must be divisible by 2&quot;]&rbrace;</span>
</div><div class="line" data-line="16">
</div><div class="line" data-line="17"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">SpecialNumber</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;15&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="18"><span style="color: #9b9ea4;">#=&gt; &lbrace;:error, [&quot;expected an integer&quot;, &quot;must be less than 10&quot;, &quot;is invalid&quot;]&rbrace;</span>
</div></code></pre>
<h2><a href="#transforming-data" aria-hidden="true" class="anchor" id="transforming-data"></a>Transforming Data</h2>
<p>You can also use the <code>raw</code> schematic to transform the data as you parse and validate it. Here we read a ISO8601 timestamp and turn it into a <code>DateTime</code> struct.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">Datetime</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Schematic</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">    <span style="color: #8cf8f7;">raw</span><span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="6">      <span style="color: #e0e2ea; font-weight: bold;">fn</span>
</div><div class="line" data-line="7">        <span style="color: #e0e2ea;">i</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:to</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #8cf8f7;">is_binary</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">i</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">and</span> <span style="color: #8cf8f7;">match?</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">DateTime</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">from_iso8601</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">i</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="8">        <span style="color: #e0e2ea;">i</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:from</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #8cf8f7;">match?</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">DateTime</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">i</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="9">      <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="10">      <span style="color: #8cf8f7;">transform: </span><span style="color: #e0e2ea; font-weight: bold;">fn</span>
</div><div class="line" data-line="11">        <span style="color: #e0e2ea;">i</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:to</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="12">          <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">dt</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">DateTime</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">from_iso8601</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">i</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="13">          <span style="color: #e0e2ea;">dt</span>
</div><div class="line" data-line="14">
</div><div class="line" data-line="15">        <span style="color: #e0e2ea;">i</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:from</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="16">          <span style="color: #e0e2ea;">DateTime</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">to_iso8601</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">i</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="17">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="18">    <span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="19">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="20"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="21">
</div><div class="line" data-line="22"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">unify</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Datetime</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;2024-07-11T17:48:41.972851Z&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="23"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok, ~U[2024-07-11 17:48:41.972851Z]&rbrace;</span>
</div></code></pre>
<h2><a href="#dumping-data" aria-hidden="true" class="anchor" id="dumping-data"></a>Dumping Data</h2>
<p>Not only can you parse and validate external data into your internal format, you can also <em>dump</em> that data back into the external format.</p>
<p>This will respect any map key transformations that you've declared and as seen above, you can use a <code>raw</code> schematic to arbitrarily control how the data is transformed in each direction.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">dump</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Datetime</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">~</span>U<span style="color: #e0e2ea;">[</span>2024-07-11 17:48:41.972851Z<span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="2"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok, &quot;2024-07-11T17:48:41.972851Z&quot;&rbrace;</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4"><span style="color: #e0e2ea;">Schematic</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">dump</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Animals</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">schematic</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">Animals</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="5">  <span style="color: #8cf8f7;">species: </span><span style="color: #b3f6c0;">&quot;lupus&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="6">  <span style="color: #8cf8f7;">genus: </span><span style="color: #b3f6c0;">&quot;canis&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">  <span style="color: #8cf8f7;">color: </span><span style="color: #b3f6c0;">&quot;grey&quot;</span>
</div><div class="line" data-line="8"><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="9"><span style="color: #9b9ea4;">#=&gt; &lbrace;:ok, %&lbrace;&quot;color&quot; =&gt; &quot;grey&quot;, &quot;genus&quot; =&gt; &quot;canis&quot;, &quot;species&quot; =&gt; &quot;lupus&quot;&rbrace;&rbrace;</span>
</div></code></pre>
<h2><a href="#future-features" aria-hidden="true" class="anchor" id="future-features"></a>Future features</h2>
<p>While the <code>oneof</code> schematic handles "union" types (typically represented like <code>Dog | Cat</code>), I would like to add "intersection" types (represented sometimes like <code>Dog &amp; Cat</code>).</p>
<h2><a href="#conclusion" aria-hidden="true" class="anchor" id="conclusion"></a>Conclusion</h2>
<p>I am pretty happy with what I've come up with, and it works for my use cases very well!</p>
<p>You can see Schematic in action in the <a href="https://github.com/elixir-tools/gen_lsp/blob/main/lib/gen_lsp/protocol/requests/initialize.ex">gen_lsp</a> code base and for an example of how it works with code generation you can check out the <a href="https://github.com/elixir-tools/lsp_codegen">lsp_codegen</a> project.</p>
<p>Happy hacking!</p> ]]></description>
    </item>
    <item>
       <title>Pet Peeves</title>
       <link>https://www.mitchellhanberg.com/pet-peeves/</link>
       <pubDate>Wed, 19 Jun 2024 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/pet-peeves/</guid>
       <description><![CDATA[ <div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title">btw</p>
<p>I update this periodically as I find new things things that bother me.</p>
</div>
<blockquote>
<p><strong>noun</strong> <em>informal</em></p>
<p><strong>something that a particular person finds especially annoying:</strong> <em>one of my biggest pet peeves is poor customer service.</em></p>
</blockquote>
<p>Here's a list of minor things I find irritating.</p>
<ul>
<li>Calling software "dead" or "abandoned"</li>
<li>Complaining about too many choices, especially when C=2</li>
<li>Complaining about what other people are doing when it doesn't affect you at all</li>
<li>Judgment without context</li>
<li>Speaking in a way that implies you have a ton of experience with something (saying "I do this in all my apps") when really you've done it two times</li>
<li>The word "just"</li>
</ul> ]]></description>
    </item>
    <item>
       <title>Create Your Own Neovim Distribution</title>
       <link>https://www.mitchellhanberg.com/create-your-own-neovim-distribution/</link>
       <pubDate>Wed, 05 Jun 2024 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/create-your-own-neovim-distribution/</guid>
       <description><![CDATA[ <p>Gotcha! The clickbait worked!</p>
<p>We're not really in the market to create an actual "distribution", but we are going to explore how to extract your Neovim configuration into it's own plugin.</p>
<p>Other real distributions actually employ this trick themselves like <a href="https://github.com/LazyVim/LazyVim">LazyVim</a> and <a href="https://github.com/astronvim/astronvim">AstroVim</a>, the main requirement is that you use the <a href="https://github.com/folke/lazy.nvim">lazy.nvim</a> package manager.</p>
<p>Let's get into the code!</p>
<h2><a href="#converting-your-current-configuration" aria-hidden="true" class="anchor" id="converting-your-current-configuration"></a>Converting your current configuration</h2>
<p>Converting your configuration into a plugin is mostly just renaming some files/directories and moving them into their own repo.</p>
<h3><a href="#original-configuration" aria-hidden="true" class="anchor" id="original-configuration"></a>Original Configuration</h3>
<p>Your original configuration might look something like this, you have a folder that handles your lazy.nvim plugin specs, some custom modules, some ftplugins, and a normal <code>init.lua</code>.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$HOME/.config/nvim/
</div><div class="line" data-line="2">└── lua/
</div><div class="line" data-line="3">    ├── custom/
</div><div class="line" data-line="4">    │   ├── plugins/
</div><div class="line" data-line="5">    │   │   ├── init.lua
</div><div class="line" data-line="6">    │   │   ├── elixir.lua
</div><div class="line" data-line="7">    │   │   └── lsp.lua
</div><div class="line" data-line="8">    │   ├── terminal.lua
</div><div class="line" data-line="9">    │   └── treesitter.lua
</div><div class="line" data-line="10">    ├── ftplugin/
</div><div class="line" data-line="11">    │   ├── elixir.lua
</div><div class="line" data-line="12">    │   └── javascript.lua
</div><div class="line" data-line="13">    └── init.lua
</div></code></pre>
<h3><a href="#plugin-based-configuration" aria-hidden="true" class="anchor" id="plugin-based-configuration"></a>Plugin based Configuration</h3>
<p>The steps I took to extract my distribution was to:</p>
<ul>
<li>Move the whole thing to a new git repository.</li>
<li>Rename <code>custom</code> to <code>mydistro</code> and resolve any necessary changes.</li>
<li>Rename <code>init.lua</code> to <code>plugin/mydistro.lua</code>.</li>
</ul>
<p>Your new configuration structure should look like this:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$HOME/
</div><div class="line" data-line="2">├── .config/nvim/
</div><div class="line" data-line="3">│   └── init.lua
</div><div class="line" data-line="4">└── mydistro/
</div><div class="line" data-line="5">    └── lua/
</div><div class="line" data-line="6">        ├── custom/
</div><div class="line" data-line="7">        │   ├── plugins/
</div><div class="line" data-line="8">        │   │   ├── init.lua
</div><div class="line" data-line="9">        │   │   ├── elixir.lua
</div><div class="line" data-line="10">        │   │   └── lsp.lua
</div><div class="line" data-line="11">        │   ├── terminal.lua
</div><div class="line" data-line="12">        │   └── treesitter.lua
</div><div class="line" data-line="13">        ├── ftplugin/
</div><div class="line" data-line="14">        │   ├── elixir.lua
</div><div class="line" data-line="15">        │   └── javascript.lua
</div><div class="line" data-line="16">        └── plugin/
</div><div class="line" data-line="17">            └── mydistro.lua
</div></code></pre>
<h3><a href="#initlua" aria-hidden="true" class="anchor" id="initlua"></a>init.lua</h3>
<p>The <code>init.lua</code> file in your dotfiles should look like this:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-lua" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;">-- bootstrap lazy.nvim</span>
</div><div class="line" data-line="2"><span style="color: #e0e2ea; font-weight: bold;">local</span> <span style="color: #e0e2ea;">lazypath</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">fn</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">stdpath</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;data&quot;</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">..</span> <span style="color: #b3f6c0;">&quot;/lazy/lazy.nvim&quot;</span>
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea; font-weight: bold;">not</span> <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">uv</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">fs_stat</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">lazypath</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">then</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea;">vim</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">system</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="6">      <span style="color: #b3f6c0;">&quot;git&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">      <span style="color: #b3f6c0;">&quot;clone&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="8">      <span style="color: #b3f6c0;">&quot;--filter=blob:none&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="9">      <span style="color: #b3f6c0;">&quot;https://github.com/folke/lazy.nvim.git&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="10">      <span style="color: #b3f6c0;">&quot;--branch=stable&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">-- latest stable release</span>
</div><div class="line" data-line="11">      <span style="color: #e0e2ea;">lazypath</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="12">    <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="13">    <span style="color: #e0e2ea;">:</span><span style="color: #8cf8f7;">wait</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="14"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="15">
</div><div class="line" data-line="16"><span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">opt</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">rtp</span><span style="color: #e0e2ea;">:</span><span style="color: #8cf8f7;">prepend</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">lazypath</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="17"><span style="color: #8cf8f7;">require</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;lazy&quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">setup</span> <span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="18">  <span style="color: #a6dbff;">spec</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="19">    <span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="20">      <span style="color: #b3f6c0;">&quot;myname/mydistro&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">-- the location on GitHub for our distro</span>
</div><div class="line" data-line="21">      <span style="color: #a6dbff;">dev</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">true</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">-- tells lazy.nvim to actually load a local copy</span>
</div><div class="line" data-line="22">      <span style="color: #a6dbff;">import</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;mydistro.plugins&quot;</span> <span style="color: #9b9ea4;">-- the path to your plugins lazy plugin spec</span>
</div><div class="line" data-line="23">    <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="24">  <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="25">  <span style="color: #a6dbff;">dev</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">&lbrace;</span> <span style="color: #a6dbff;">path</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;~&quot;</span> <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">-- the path to where `dev = true` looks for local plugins</span>
</div><div class="line" data-line="26">  <span style="color: #a6dbff;">install</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="27">    <span style="color: #a6dbff;">missing</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">true</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="28">  <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="29"><span style="color: #8cf8f7;">&rbrace;</span>
</div></code></pre>
<p>And we're done!</p>
<h2><a href="#why" aria-hidden="true" class="anchor" id="why"></a>Why?</h2>
<p>Well, one: for fun!</p>
<p>And two: I personally use <a href="https://github.com/nix-community/home-manager">home-manager</a> to manage my dotfiles, but that requires running home-manager anytime you change them.</p>
<p>This is very annoying when it comes to tweaking your Neovim configuration, so moving my configuration to a plugin that lives outside of home-manager means I can iterate quicker.</p>
<p>Another hypothetical use case is making it easier for someone to try out your Neovim configuration. This could be for someone getting into Neovim for the first time, or perhaps a plugin author trying to help debug an issue you're having.</p>
<p>Happy hacking!</p> ]]></description>
    </item>
    <item>
       <title>Modern Format on Save in Neovim</title>
       <link>https://www.mitchellhanberg.com/modern-format-on-save-in-neovim/</link>
       <pubDate>Thu, 14 Mar 2024 08:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/modern-format-on-save-in-neovim/</guid>
       <description><![CDATA[ <p>Formatting on save is a popular workflow and is built into many text editors and IDEs.</p>
<p>In Neovim, you must create this manually, but it is very easy using <code>autocmds</code>.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-lua" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;">-- 1</span>
</div><div class="line" data-line="2"><span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">api</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">nvim_create_autocmd</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;LspAttach&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="3">  <span style="color: #a6dbff;">group</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">api</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">nvim_create_augroup</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;lsp&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">&lbrace;</span> <span style="color: #a6dbff;">clear</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">true</span> <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">callback</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea; font-weight: bold;">function</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">args</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="5">    <span style="color: #9b9ea4;">-- 2</span>
</div><div class="line" data-line="6">    <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">api</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">nvim_create_autocmd</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;BufWritePre&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="7">      <span style="color: #9b9ea4;">-- 3</span>
</div><div class="line" data-line="8">      <span style="color: #a6dbff;">buffer</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">args</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">buf</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="9">      <span style="color: #8cf8f7;">callback</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea; font-weight: bold;">function</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="10">        <span style="color: #9b9ea4;">-- 4 + 5</span>
</div><div class="line" data-line="11">        <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">lsp</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">buf</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">format</span> <span style="color: #8cf8f7;">&lbrace;</span><span style="color: #a6dbff;">async</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">false</span><span style="color: #e0e2ea;">,</span> <span style="color: #a6dbff;">id</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">args</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">data</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">client_id</span> <span style="color: #8cf8f7;">&rbrace;</span>
</div><div class="line" data-line="12">      <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="13">    <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="14">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="15"><span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<ol>
<li>We create a new <code>autocmd</code> that fires on the <code>LspAttach</code> event. This event is fired when an LSP client attaches to a buffer. In this <code>autocmd</code>, you can easily set configuration that is specific to that buffer and LSP client.</li>
<li>We create another <code>autcmd</code> inside the <code>LspAttach</code> callback, this time for the <code>BufWritePre</code> event. This fires when you save the buffer but before it flushes anything to disk. This gives you a chance to manipulate the buffer first.</li>
<li>We configure this <code>autocmd</code> to only execute for the current buffer. This is a little more straight forward rather than setting an <code>autocmd</code> that executes for any range of file types.</li>
<li>In the callback, we run <code>vim.lsp.buf.format</code> to format the buffer, with the flag <code>async = false</code>. This ensures that the formatting request will block until it completes, so that it completely finishes formatting before flushing the file to disk.</li>
<li>We also set the <code>id = args.data.client</code> flag so that the formatting request is only sent to the LSP client that is related to the outer <code>LspAttach</code> request. This ensures that we aren't running the formatting request multiple times in case the buffer is attached to multiple LSPs.</li>
</ol>
<p>And there we have it, modern format on save for those who want it.</p> ]]></description>
    </item>
    <item>
       <title>Ergonomic Remote Development</title>
       <link>https://www.mitchellhanberg.com/ergonomic-remote-development/</link>
       <pubDate>Tue, 07 Nov 2023 08:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/ergonomic-remote-development/</guid>
       <description><![CDATA[ <p>I recently built a new PC (you can see the specs on my <a href="/uses">/uses</a> page!) and installed Linux on it.</p>
<p>The way I have been using it mostly has been through an SSH connection, even though its sitting underneath my desk, plugged into my monitor, and all my peripherals plugged into a nifty USB hub splitter thing!</p>
<p>I simply couldn't live without the macOS desktop environment. I have so much muscle memory for all the shortcuts and have so many apps that I use that enhance my workflow.</p>
<p>I have never remotely developed for this long before, so I quickly ran into a bunch of paper cuts for which I was able to easily find bandaids.</p>
<h2><a href="#tailscale" aria-hidden="true" class="anchor" id="tailscale"></a>Tailscale</h2>
<p>When you are going to be SSHing into a remote computer, you need to know the IP address of the computer.</p>
<p><a href="https://tailscale.com/">Tailscale</a> makes this super easy. You install Tailscale on your local computer and on the remote computer, and they both join your "tailnet".</p>
<p>Now, you have a static IP address that only other computers on your tailnet can access!</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">$</span> <span style="color: #e0e2ea;">ssh</span> <span style="color: #e0e2ea;">mitchell@</span><span style="color: #e0e2ea;">&lt;</span><span style="color: #8cf8f7;">remote</span> <span style="color: #8cf8f7;">ip</span><span style="color: #e0e2ea;">&gt;</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #8cf8f7;">Welcome</span> <span style="color: #8cf8f7;">to</span> <span style="color: #8cf8f7;">Ubuntu</span> <span style="color: #8cf8f7;">23.10</span> <span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">GNU/Linux</span> <span style="color: #e0e2ea;">6.5.0-10-generic</span> <span style="color: #e0e2ea;">x86_64</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"> <span style="color: #8cf8f7;">*</span> <span style="color: #e0e2ea;">Documentation:</span>  <span style="color: #e0e2ea;">https://help.ubuntu.com</span>
</div><div class="line" data-line="6"> <span style="color: #8cf8f7;">*</span> <span style="color: #e0e2ea;">Management:</span>     <span style="color: #e0e2ea;">https://landscape.canonical.com</span>
</div><div class="line" data-line="7"> <span style="color: #8cf8f7;">*</span> <span style="color: #e0e2ea;">Support:</span>        <span style="color: #e0e2ea;">https://ubuntu.com/advantage</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9"><span style="color: #e0e2ea;">6</span> <span style="color: #e0e2ea;">updates</span> <span style="color: #e0e2ea;">can</span> <span style="color: #e0e2ea;">be</span> <span style="color: #e0e2ea;">applied</span> <span style="color: #e0e2ea;">immediately.</span>
</div><div class="line" data-line="10"><span style="color: #8cf8f7;">To</span> <span style="color: #e0e2ea;">see</span> <span style="color: #e0e2ea;">these</span> <span style="color: #e0e2ea;">additional</span> <span style="color: #e0e2ea;">updates</span> <span style="color: #e0e2ea;">run:</span> <span style="color: #e0e2ea;">apt</span> <span style="color: #e0e2ea;">list</span> <span style="color: #e0e2ea;">--upgradable</span>
</div><div class="line" data-line="11">
</div><div class="line" data-line="12"><span style="color: #8cf8f7;">***</span> <span style="color: #e0e2ea;">System</span> <span style="color: #e0e2ea;">restart</span> <span style="color: #e0e2ea;">required</span> <span style="color: #e0e2ea;">***</span>
</div><div class="line" data-line="13"><span style="color: #8cf8f7;">Last</span> <span style="color: #e0e2ea;">login:</span> <span style="color: #e0e2ea;">Tue</span> <span style="color: #e0e2ea;">Nov</span>  <span style="color: #e0e2ea;">7</span> <span style="color: #e0e2ea;">07:24:00</span> <span style="color: #e0e2ea;">2023</span> <span style="color: #e0e2ea;">from</span> <span style="color: #e0e2ea;">&lt;</span><span style="color: #8cf8f7;">local</span> <span style="color: #8cf8f7;">ip</span><span style="color: #e0e2ea;">&gt;</span><span style="color: #8cf8f7;"></span>
</div></code></pre>
<h2><a href="#clipboard" aria-hidden="true" class="anchor" id="clipboard"></a>Clipboard</h2>
<p>You'll want to make sure that you can still copy/paste to/from your host computer's clipboard (or 'pasteboard' as macOS calls it) from within TUI applications like Vim/Tmux and in shell scripts.</p>
<h3><a href="#tui-apps" aria-hidden="true" class="anchor" id="tui-apps"></a>TUI Apps</h3>
<p>If you are using a modern terminal emulator (I use <a href="https://mitchellh.com/ghostty">Ghostty</a>), you most likely have clipboard working already for TUI applications, as long as they support the appropriate terminal features.</p>
<p>I use <a href="https://neovim.io/">Neovim</a>, which does not <a href="https://github.com/neovim/neovim/pull/25872">currently</a> support the OSC-52 terminal feature that enables system clipboard communication, but I use Neovim inside <a href="https://github.com/tmux/tmux">tmux</a>, which does!</p>
<p>And luckily, Neovim has a tmux 'clipboard provider' which is how Neovim actually communicates to the "outside" clipboard.</p>
<p>If you don't use tmux and use Vim/Neovim, there are plenty of <a href="https://github.com/ojroques/vim-oscyank">plugins</a> that implement it!</p>
<h3><a href="#scripting" aria-hidden="true" class="anchor" id="scripting"></a>Scripting</h3>
<p>The way that I have implemented scripting to my local clipboard is to again use SSH!</p>
<p>Since your computers are using Tailscale and are on the same tailnet, this means that your local computer also has a nice and static IP address that you can use.</p>
<p>Here is an example:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">$</span> <span style="color: #e0e2ea;">ssh</span> <span style="color: #e0e2ea;">mitchell@</span><span style="color: #e0e2ea;">&lt;</span><span style="color: #8cf8f7;">local</span> <span style="color: #8cf8f7;">ip</span><span style="color: #e0e2ea;">&gt;</span> <span style="color: #8cf8f7;">pbpaste</span> \
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">|</span> <span style="color: #8cf8f7;">some-local-command</span> \
</div><div class="line" data-line="3">  <span style="color: #e0e2ea;">|</span> <span style="color: #8cf8f7;">ssh</span> <span style="color: #e0e2ea;">mitchell@</span><span style="color: #e0e2ea;">&lt;</span><span style="color: #8cf8f7;">local</span> <span style="color: #8cf8f7;">ip</span><span style="color: #e0e2ea;">&gt;</span> <span style="color: #8cf8f7;">pbcopy</span>
</div></code></pre>
<p>Here, we remotely run the <code>pbpaste</code> command (which, along with <code>pbpaste</code>, are the macOS scripting utilities for using the system clipboard), pipe the ouput into a local command, and then pipe it into <code>ssh</code>, which will send it to the <code>pbcopy</code> command on our local computer.</p>
<p>Now, you can easily wrap that up in an alias or a shell script.</p>
<h2><a href="#opening-your-web-browser" aria-hidden="true" class="anchor" id="opening-your-web-browser"></a>Opening your web browser</h2>
<p>If you are used to opening web pages with utilities like the GitHub CLI <a href="https://cli.github.com/">gh</a>, you'll notice that those don't work anymore. This is because (on Linux), the tool is (most likely) running <code>xdg-open</code>, which is similar to the <code>open</code> command on macOS.</p>
<p>You are running this on the remote computer, which is not logged into a desktop environment, and has no web browser.</p>
<p>Well, we can easily fix this again with SSH!</p>
<p>What I did was write a shell script called <code>xdg-open</code> and put it in my <code>$PATH</code>. This way, tools like <code>gh</code> will actually call my script instead of the built-in <code>xdg-open</code>.</p>
<p>My script looks like this:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">#!/usr/bin/env bash</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">function</span> <span style="color: #8cf8f7;">main</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="4">	<span style="color: #e0e2ea; font-weight: bold;">local</span> <span style="color: #e0e2ea;">ip</span>
</div><div class="line" data-line="5">	<span style="color: #e0e2ea; font-weight: bold;">local</span> <span style="color: #e0e2ea;">uri</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7">	<span style="color: #e0e2ea;">uri</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$</span><span style="color: #e0e2ea;">1</span>&quot;</span>
</div><div class="line" data-line="8">	<span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">[[</span> <span style="color: #e0e2ea;">!</span> <span style="color: #e0e2ea;">-z</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$</span><span style="color: #e0e2ea;">SSH_CONNECTION</span>&quot;</span> <span style="color: #e0e2ea;">]]</span><span style="color: #e0e2ea;">;</span> <span style="color: #e0e2ea; font-weight: bold;">then</span>
</div><div class="line" data-line="9">		<span style="color: #e0e2ea;">ip</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$(</span><span style="color: #8cf8f7;">echo</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$</span><span style="color: #e0e2ea;">SSH_CONNECTION</span>&quot;</span> <span style="color: #e0e2ea;">|</span> <span style="color: #8cf8f7;">awk</span> <span style="color: #b3f6c0;">&#39;&lbrace; print $1 &rbrace;&#39;</span><span style="color: #8cf8f7;">)</span>&quot;</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11">		<span style="color: #8cf8f7;">ssh</span> <span style="color: #b3f6c0;">&quot;mitchell@<span style="color: #8cf8f7;">$</span><span style="color: #e0e2ea;">ip</span>&quot;</span> <span style="color: #e0e2ea;">open</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$</span><span style="color: #e0e2ea;">uri</span>&quot;</span>
</div><div class="line" data-line="12">	<span style="color: #e0e2ea; font-weight: bold;">else</span>
</div><div class="line" data-line="13">		<span style="color: #8cf8f7;">xdg-open</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$</span><span style="color: #e0e2ea;">uri</span>&quot;</span>
</div><div class="line" data-line="14">	<span style="color: #e0e2ea; font-weight: bold;">fi</span>
</div><div class="line" data-line="15"><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="16">
</div><div class="line" data-line="17"><span style="color: #8cf8f7;">main</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$</span><span style="color: #e0e2ea;">@</span>&quot;</span>
</div></code></pre>
<p>We check the <code>$SSH_CONNECTION</code> environment variable to see if we are in an SSH session.</p>
<p>If we are <em>not</em>, we just call <code>xdg-open</code> as usual. This is helpful in case we log onto our PC like a normal person.</p>
<p>If we <em>are</em>, then we parse our local IP address out of the <code>$SSH_CONNECTION</code> variable and run the <code>open</code> command remotely using <code>ssh</code>.</p>
<h2><a href="#web-development" aria-hidden="true" class="anchor" id="web-development"></a>Web Development</h2>
<p>Now, if you are a web developer, you typically will be starting a local web server and previewing your site or app in the browser. This gets a little tricky when your browser and web server are not on the same computer!</p>
<h3><a href="#tailscale-1" aria-hidden="true" class="anchor" id="tailscale-1"></a>Tailscale</h3>
<p>Luckily, tailscale comes to the rescue again!</p>
<p>With tailscale, you can create a reverse proxy served over <code>https</code> with your existing free plan.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">$</span> <span style="color: #e0e2ea;">tailscale</span> <span style="color: #e0e2ea;">serve</span> <span style="color: #e0e2ea;">https</span> <span style="color: #e0e2ea;">/</span> <span style="color: #e0e2ea;">http://localhost:4000</span>
</div></code></pre>
<p>This will create a reverse proxy that is only available inside your tailnet!</p>
<p>If you want to show off your beautiful website to a client or coworker, you can run <code>tailscale funnel 443 on</code>, which will make your reverse proxy available outside of your tailnet.</p>
<h3><a href="#ssh-port-forwarding" aria-hidden="true" class="anchor" id="ssh-port-forwarding"></a>SSH Port Forwarding</h3>
<p>You can also just tack on some options to the <code>ssh</code> command instead of using Tailscale</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">$</span> <span style="color: #e0e2ea;">ssh</span> <span style="color: #e0e2ea;">mitchell@</span><span style="color: #e0e2ea;">&lt;</span><span style="color: #8cf8f7;">remote</span> <span style="color: #8cf8f7;">ip</span><span style="color: #e0e2ea;">&gt;</span> <span style="color: #8cf8f7;">-L</span> <span style="color: #8cf8f7;">4999:localhost:4999</span>
</div></code></pre>
<h2><a href="#signing-git-commits" aria-hidden="true" class="anchor" id="signing-git-commits"></a>Signing Git Commits</h2>
<p>I sign my <code>git</code> commits using the <a href="https://1password.com/">1Password</a> SSH agent. This allows me to use an SSH key (instead of a GPG key) and to authorize the usage of the key with Touch ID on my Mac.</p>
<p>As you can imagine, when I went commit my first code on my new PC, I thought I had found a roadblock kill this new workflow.</p>
<p>Luckily, you can actually <em>forward</em> your SSH agent when you make and SSH connection. This is typically for when you are machine hopping and have to <code>ssh</code> to a remote machine from inside an <code>ssh</code> connection, so that you don't have to install your keys on your local computer and the first remote computer.</p>
<p>And, this also works for 1Password Git Commit signing!</p>
<p>Just add an entry in your <code>~/.ssh/config</code> for your PC to allow forwarding on your local computer:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">Host &lt;remote ip&gt;
</div><div class="line" data-line="2">  ForwardAgent yes
</div></code></pre>
<p>And on the PC, configure the IdentityAgent to use 1Password. I don't actually remember if this is necessary, but when writing this post, I found this config and I don't remember writing it, so it must be important!</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">Host *
</div><div class="line" data-line="2">  IdentityAgent ~/.1password/agent.sock
</div></code></pre>
<h2><a href="#conclusion" aria-hidden="true" class="anchor" id="conclusion"></a>Conclusion</h2>
<p>And there we have it!</p>
<p>Now you should be able to develop remotely and not even be able to tell the difference, except for the sheer speed of your overpowered new PC!</p> ]]></description>
    </item>
    <item>
       <title>OTP Process Abstractions with proc_lib</title>
       <link>https://www.mitchellhanberg.com/otp-process-abstractions-with-proc-lib/</link>
       <pubDate>Mon, 22 May 2023 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/otp-process-abstractions-with-proc-lib/</guid>
       <description><![CDATA[ <p>In November of 2022, I had the privilege to speak at <a href="https://codebeamamerica.com/">Code BEAM America</a>.</p>
<p>You can check out the video below, let me know what you think!</p>
<p> </p>
<div class="flex justify-center">
  <iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/Ug-SEozyG1A" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div> ]]></description>
    </item>
    <item>
       <title>Credo Language Server and the birth of elixir-tools</title>
       <link>https://www.mitchellhanberg.com/credo-language-server-and-the-birth-of-elixir-tools/</link>
       <pubDate>Tue, 18 Apr 2023 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/credo-language-server-and-the-birth-of-elixir-tools/</guid>
       <description><![CDATA[ <p>Last year I started working on <a href="https://github.com/mhanberg/gen_lsp">gen_lsp</a>, an abstraction for writing <a href="https://microsoft.github.io/language-server-protocol/">language servers</a> in Elixir.</p>
<blockquote>
<p>I gave a presentation on gen_lsp and writing OTP process abstractions at CodeBEAM America 2022. You can watch my talk on <a href="https://www.youtube.com/watch?v=Ug-SEozyG1A">YouTube</a>.</p>
</blockquote>
<p>Today I'd like to announce the release of the first language server built with <code>gen_lsp</code>.</p>
<h2><a href="#credo-language-server" aria-hidden="true" class="anchor" id="credo-language-server"></a>Credo Language Server</h2>
<p><a href="https://github.com/elixir-tools/credo-language-server">Credo Language Server</a> is a persistent server that communicates with text editors via the Language Server Protocol.</p>
<p>This initial release comes with project wide diagnostics and a code action to add a <code># credo:disable-for-next-line</code> magic comment above any Credo warning.</p>
<p>You can install it today using one of the <code>elixir-tools</code> family of editor extensions.</p>
<h2><a href="#elixir-tools" aria-hidden="true" class="anchor" id="elixir-tools"></a>elixir-tools</h2>
<p><a href="https://github.com/elixir-tools">elixir-tools</a> is the new home for Credo Language Server and the aforementioned editor extensions.</p>
<h3><a href="#elixir-toolsnvim" aria-hidden="true" class="anchor" id="elixir-toolsnvim"></a>elixir-tools.nvim</h3>
<p>If you write Elixir and use <a href="https://neovim.io">Neovim</a>, there is a chance you are already using my plugin, <a href="https://github.com/elixir-tools/elixir-tools.nvim">elixir-tools.nvim</a> (formerly known as elixir.nvim).</p>
<p>The Neovim plugin has been renamed to help it better fit into it's place in the ecosystem and has given rise to the new elixir-tools family.</p>
<h3><a href="#elixir-toolsvscode" aria-hidden="true" class="anchor" id="elixir-toolsvscode"></a>elixir-tools.vscode</h3>
<p>The elixir-tools family keeps on growing!</p>
<p>The release of Credo Language Server naturally mean that there would need to a way to use it with Visual Studio Code, so <a href="https://marketplace.visualstudio.com/items?itemName=elixir-tools.elixir-tools">elixir-tools.vscode</a> was born.</p>
<p>This initial release brings support for Credo Language Server and Elixir filetype and highlighting support.</p>
<p><a href="https://github.com/elixir-lsp/elixir-ls">ElixirLS</a> support has been omitted since it can happily coexist along side elixir-tools.vscode. If there is demand to add ElixirLS support, that can be done, but at this time there is no need.</p>
<h2><a href="#slow-and-steady-wins-the-race" aria-hidden="true" class="anchor" id="slow-and-steady-wins-the-race"></a>Slow and Steady Wins the Race</h2>
<p>Over the last 10 months I have been slowly chipping away at this project, making sure that every part is built with excellence in mind.</p>
<p>As I built out gen_lsp, I realized that the best way to achieve correctness was to generate most of the code from the official specification.</p>
<p>So, I built the <a href="https://github.com/mhanberg/lsp_codegen">lsp_codegen</a> library.</p>
<p>It includes a handwritten generator that conforms to the LSP specification's <a href="https://json-schema.org">JSON Schema</a> and is used to read the LSP metamodel and to generate Elixir code that includes typespecs and structs for all the data structures.</p>
<p>While building the code generator, I realized that the specification's "Or" type was easy to represent using typespecs, but was actually hard to deserialize from a JSON payload into the Elixir data structures.</p>
<p>Inspired by <a href="https://github.com/elixir-toniq/norm">norm</a> and my friend <a href="https://keathley.io">Chris Keathley</a>, I built <a href="https://github.com/mhanberg/schematic">schematic</a>. This allows me to fully validate, serialize, and deserialize complex data structures.</p>
<p>So, to build Credo Language Server, it just took 3 libraries, 2 editor extensions, and a conference talk. 😅</p>
<p>I will be writing more about gen_lsp and schematic in the future.</p>
<h2><a href="#whats-next" aria-hidden="true" class="anchor" id="whats-next"></a>What's Next</h2>
<p>This is just the beginning! 🤗</p>
<p>I have some exciting ideas planned and can't wait to be able to share them with the Elixir community. If you'd like to stay on the bleeding edge of elixir-tools, feel free to join the <a href="https://discord.gg/6XdGnxVA2A">Discord</a>.</p> ]]></description>
    </item>
    <item>
       <title>Introducing lazyasdf: An Elixir-based TUI for the asdf version manager</title>
       <link>https://www.mitchellhanberg.com/introducing-lazyasdf-a-tui-for-the-asdf-version-manager/</link>
       <pubDate>Mon, 06 Mar 2023 01:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/introducing-lazyasdf-a-tui-for-the-asdf-version-manager/</guid>
       <description><![CDATA[ <p><img src="https://res.cloudinary.com/mhanberg/image/upload/v1678034860/CleanShot_2023-03-04_at_14.44.18_2x.png" alt="lazyasdf" /></p>
<p><a href="https://github.com/mhanberg/lazyasdf">lazyasdf</a> is my first real<sup class="footnote-ref"><a href="#fn-1" id="fnref-1" data-footnote-ref>1</a></sup> attempt at making a TUI with <a href="https://elixir-lang.org">Elixir</a>!</p>
<p><a href="https://asdf-vm.com/">asdf</a> is normally used through a <em>command line interface</em> (CLI), <code>lazyasdf</code> presents you with a <em>terminal user interface</em> (TUI) for working with <code>asdf</code>.</p>
<p>I recently fell in love with <a href="https://github.com/jesseduffield/lazygit">lazygit</a> and have since dreamed of writing my own TUI programs, but with Elixir.</p>
<p>The TUI provides a quick and intuitive interface for those familiar with the terminal and for those who prefer a graphical application, but the TUI is so much more approachable in my humble opinion when it comes to making your own 😄.</p>
<p>While I find <code>lazyasdf</code> to be an amazing achievement for myself, it isn't <em>super</em> interesting on its own. Let's dive into the specifics of how I was able to build and distribute a TUI application with Elixir.</p>
<h2><a href="#ratatouille" aria-hidden="true" class="anchor" id="ratatouille"></a>Ratatouille</h2>
<p>None of this would be possible if it weren't for the library <a href="https://github.com/ndreynolds/ratatouille">ratatouille</a> by <a href="https://ndreynolds.com/">Nick Reynolds</a>.</p>
<p>I am not some genius when it comes to terminals or laying out text, this all comes from Ratatouille, which builds off of <a href="https://github.com/nsf/termbox">termbox</a>, which is a [n]curses alternative.</p>
<p>Ratatouille leverages the <a href="https://guide.elm-lang.org/architecture/">Elm Architecture</a> of which many of us have grown familiar. Let's take a look at a small Ratatouille program that showcases most of its features.</p>
<p><img src="https://res.cloudinary.com/mhanberg/image/upload/v1678037282/CleanShot_2023-03-05_at_12.27.43_2x.png" alt="Tuido" /></p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;">#!/usr/bin/env elixir</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea;">Mix</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">install</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">:ratatouille</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">Todos</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">behaviour <span style="color: #e0e2ea;">Ratatouille.App</span></span></span></span></span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Ratatouille.View</span>
</div><div class="line" data-line="9">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Ratatouille.Constants</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">only: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">color: </span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">key: </span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11">  <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">style_selected <span style="color: #e0e2ea;">[</span></span></span></span></span>
</div><div class="line" data-line="12"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;"><span style="color: #8cf8f7;"><span style="color: #e0e2ea;">    <span style="color: #8cf8f7;">color: </span><span style="color: #8cf8f7;">color</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">:black</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span></span></span></span></span>
</div><div class="line" data-line="13"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;"><span style="color: #8cf8f7;"><span style="color: #e0e2ea;">    <span style="color: #8cf8f7;">background: </span><span style="color: #8cf8f7;">color</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">:white</span><span style="color: #e0e2ea;">)</span></span></span></span></span>
</div><div class="line" data-line="14"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;"><span style="color: #8cf8f7;"><span style="color: #e0e2ea;">  <span style="color: #e0e2ea;">]</span></span></span></span></span>
</div><div class="line" data-line="15">
</div><div class="line" data-line="16">  <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">space <span style="color: #8cf8f7;">key</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">:space</span><span style="color: #e0e2ea;">)</span></span></span></span></span>
</div><div class="line" data-line="17">
</div><div class="line" data-line="18">  <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">impl <span style="color: #e0e2ea;">true</span></span></span></span></span>
</div><div class="line" data-line="19">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">init</span><span style="color: #e0e2ea;">(</span><span style="color: #9b9ea4;">_</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="20">    <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="21">      <span style="color: #8cf8f7;">todo: </span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="22">        <span style="color: #8cf8f7;">items: </span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #b3f6c0;">&quot;buy eggs&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">false</span> <span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;mow the lawn&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">true</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;get a haircut&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">false</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="23">        <span style="color: #8cf8f7;">cursor_y: </span><span style="color: #e0e2ea;">0</span>
</div><div class="line" data-line="24">      <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="25">    <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="26">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="27">
</div><div class="line" data-line="28">  <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">impl <span style="color: #e0e2ea;">true</span></span></span></span></span>
</div><div class="line" data-line="29">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">update</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">model</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">msg</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="30">    <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #e0e2ea;">msg</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="31">      <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:event</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">key: </span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">space</span></span></span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="32">        <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">todo</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_done</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">at</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">model</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">todo</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">model</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">todo</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">cursor_y</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="33">
</div><div class="line" data-line="34">        <span style="color: #8cf8f7;">update_in</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">model</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">todo</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">todo</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&amp;</span> <span style="color: #e0e2ea;">!</span><span style="color: #e0e2ea;">&amp;</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="35">
</div><div class="line" data-line="36">      <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:event</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">ch: </span><span style="color: #e0e2ea;">?j</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="37">        <span style="color: #8cf8f7;">update_in</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">model</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">todo</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">cursor_y</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&amp;</span><span style="color: #8cf8f7;">cursor_down</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&amp;</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">model</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">todo</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="38">
</div><div class="line" data-line="39">      <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:event</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">ch: </span><span style="color: #e0e2ea;">?k</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="40">        <span style="color: #8cf8f7;">update_in</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">model</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">todo</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">cursor_y</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&amp;</span><span style="color: #8cf8f7;">cursor_up</span><span style="color: #e0e2ea;">/</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="41">
</div><div class="line" data-line="42">      <span style="color: #9b9ea4;">_</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="43">        <span style="color: #e0e2ea;">model</span>
</div><div class="line" data-line="44">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="45">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="46">
</div><div class="line" data-line="47">  <span style="color: #e0e2ea; font-weight: bold;">defp</span> <span style="color: #8cf8f7;">cursor_down</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">cursor</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">rows</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="48">    <span style="color: #8cf8f7;">min</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">cursor</span> <span style="color: #e0e2ea;">+</span> <span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">count</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">rows</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">-</span> <span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="49">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="50">
</div><div class="line" data-line="51">  <span style="color: #e0e2ea; font-weight: bold;">defp</span> <span style="color: #8cf8f7;">cursor_up</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">cursor</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="52">    <span style="color: #8cf8f7;">max</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">cursor</span> <span style="color: #e0e2ea;">-</span> <span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="53">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="54">
</div><div class="line" data-line="55">  <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">impl <span style="color: #e0e2ea;">true</span></span></span></span></span>
</div><div class="line" data-line="56">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">render</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">model</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="57">    <span style="color: #8cf8f7;">view</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="58">      <span style="color: #8cf8f7;">panel</span> <span style="color: #8cf8f7;">title: </span><span style="color: #b3f6c0;">&quot;TODO&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="59">        <span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">t</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">done</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">idx</span> <span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">with_index</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">model</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">todo</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="60">          <span style="color: #8cf8f7;">row</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="61">            <span style="color: #8cf8f7;">column</span> <span style="color: #8cf8f7;">size: </span><span style="color: #e0e2ea;">12</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="62">              <span style="color: #8cf8f7;">label</span> <span style="color: #e0e2ea; font-weight: bold;">if</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">idx</span> <span style="color: #e0e2ea;">==</span> <span style="color: #e0e2ea;">model</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">todo</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">cursor_y</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">style_selected</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">else: </span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="63">                <span style="color: #8cf8f7;">text</span> <span style="color: #8cf8f7;">content: </span><span style="color: #b3f6c0;">&quot;- [&quot;</span>
</div><div class="line" data-line="64">                <span style="color: #8cf8f7;">done</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">done</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="65">                <span style="color: #8cf8f7;">text</span> <span style="color: #8cf8f7;">content: </span><span style="color: #b3f6c0;">&quot;] <span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">t</span><span style="color: #8cf8f7;">&rbrace;</span>&quot;</span>
</div><div class="line" data-line="66">              <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="67">            <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="68">          <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="69">        <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="70">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="71">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="72">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="73">
</div><div class="line" data-line="74">  <span style="color: #e0e2ea; font-weight: bold;">defp</span> <span style="color: #8cf8f7;">done</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">true</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #8cf8f7;">text</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">content: </span><span style="color: #b3f6c0;">&quot;x&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="75">  <span style="color: #e0e2ea; font-weight: bold;">defp</span> <span style="color: #8cf8f7;">done</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">false</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #8cf8f7;">text</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">content: </span><span style="color: #b3f6c0;">&quot; &quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="76"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="77">
</div><div class="line" data-line="78"><span style="color: #e0e2ea;">Ratatouille</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">run</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Todos</span><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<p>You should be able to copy the above snippet into a file, make it executable (<code>chmod + x</code>) and run it!</p>
<p>Ratatouille calls for 3 callbacks in your TUI program, <code>init/1</code>, <code>update/2</code>, and <code>render/1</code>.</p>
<ul>
<li>
<p>When the program boots up, the <code>init/1</code> callback is called and the return value becomes your initial model state.</p>
</li>
<li>
<p>Whenever the TUI receives user input, the <code>update/2</code> callback is executed with the message and your current model state.</p>
</li>
<li>
<p>When that returns, the runtime will call the <code>render/1</code> callback with the new model state.</p>
<p>The <code>render/1</code> callback is full of macros which translate to element structs, so it's just an ergonomic DSL. Typing out many structs by hand would be a PITA!</p>
</li>
</ul>
<h3><a href="#notes" aria-hidden="true" class="anchor" id="notes"></a>Notes</h3>
<p>You have probably observed that, while it is high level compared to raw <code>termbox</code>, Ratatouille is still sort of "low level" as an application framework.</p>
<p>We still have to manually track and move our cursor position, as well as index into our data structures to pull out the right data for that position.</p>
<h2><a href="#burrito" aria-hidden="true" class="anchor" id="burrito"></a>Burrito</h2>
<p>Now the normal problem with Elixir apps is that you have to have Elixir and Erlang on your machine to run them, as well as keep track of the version of them you have installed to make sure they are compatible, as well as write aliases to run escripts and Mix tasks, yada yada.</p>
<p>This is where <a href="https://github.com/burrito-elixir/burrito">Burrito</a> comes in!</p>
<p>Burrito utilizes <a href="https://ziglang.org/">Zig</a> to bundle up your application, the BEAM, and the Runtime all into one tidy executable that you can distribute at your leisure!</p>
<p>In the end, once we run <code>MIX_ENV=prod mix release</code>, Burrito will create binaries for each of our specified target platforms, and you can just copy those onto your computer and run them</p>
<p>The Burrito project is lead by <a href="https://twitter.com/doawoo/">Digit</a>.</p>
<h2><a href="#homebrew" aria-hidden="true" class="anchor" id="homebrew"></a>Homebrew</h2>
<p>To make any program useful, it is help to be able to install it easily.</p>
<p>Homebrew is the primary way of accomplishing this on MacOS (my preferred operating system) and you can easily host your own collection of Homebrew packages with your own Tap!</p>
<p>Since <code>lazyasdf</code> has some quirky dependencies, the formula (what Homebrew calls a package definition) is a little interesting.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-ruby" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">class</span> <span style="color: #e0e2ea;">Lazyasdf</span> <span style="color: #e0e2ea;">&lt;</span> <span style="color: #e0e2ea;">Formula</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">desc</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">TUI for the asdf version manager</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="3">  <span style="color: #8cf8f7;">homepage</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">https://github.com/mhanberg/lazyasdf</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">url</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">https://github.com/mhanberg/lazyasdf/archive/refs/tags/v0.1.1.tar.gz</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="5">  <span style="color: #8cf8f7;">sha256</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">787da19809ed714c569c8bd7df58d55d7389b69efdf1859e57f713d18e3d2d05</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="6">  <span style="color: #8cf8f7;">license</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">MIT</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8">  <span style="color: #8cf8f7;">bottle</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="9">    <span style="color: #8cf8f7;">root_url</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">https://github.com/mhanberg/homebrew-tap/releases/download/lazyasdf-0.1.1</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="10">    <span style="color: #8cf8f7;">sha256</span> <span style="color: #8cf8f7;">cellar</span><span style="color: #e0e2ea;">:</span> <span style="color: #8cf8f7;">:any_skip_relocation</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">monterey</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">f489e328c19954d62284a7154fbc8da4e7a1df61dc963930d291361a7b2ca751</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="11">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="12">
</div><div class="line" data-line="13">  <span style="color: #8cf8f7;">depends_on</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">elixir</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">:build</span>
</div><div class="line" data-line="14">  <span style="color: #8cf8f7;">depends_on</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">erlang</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">:build</span>
</div><div class="line" data-line="15">  <span style="color: #8cf8f7;">depends_on</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">gcc</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">:build</span>
</div><div class="line" data-line="16">  <span style="color: #8cf8f7;">depends_on</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">make</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">:build</span>
</div><div class="line" data-line="17">  <span style="color: #8cf8f7;">depends_on</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">python@3.9</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">:build</span>
</div><div class="line" data-line="18">  <span style="color: #8cf8f7;">depends_on</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">xz</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">:build</span>
</div><div class="line" data-line="19">
</div><div class="line" data-line="20">  <span style="color: #8cf8f7;">depends_on</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">asdf</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="21">
</div><div class="line" data-line="22">  <span style="color: #8cf8f7;">on_macos</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="23">    <span style="color: #8cf8f7;">on_arm</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="24">      <span style="color: #8cf8f7;">resource</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">zig</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="25">        <span style="color: #8cf8f7;">url</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">https://ziglang.org/download/0.10.0/zig-macos-aarch64-0.10.0.tar.xz</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="26">        <span style="color: #8cf8f7;">sha256</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">02f7a7839b6a1e127eeae22ea72c87603fb7298c58bc35822a951479d53c7557</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="27">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="28">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="29">
</div><div class="line" data-line="30">    <span style="color: #8cf8f7;">on_intel</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="31">      <span style="color: #8cf8f7;">resource</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">zig</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="32">        <span style="color: #8cf8f7;">url</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">https://ziglang.org/download/0.10.0/zig-macos-x86_64-0.10.0.tar.xz</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="33">        <span style="color: #8cf8f7;">sha256</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">3a22cb6c4749884156a94ea9b60f3a28cf4e098a69f08c18fbca81c733ebfeda</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="34">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="35">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="36">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="37">
</div><div class="line" data-line="38">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">install</span>
</div><div class="line" data-line="39">    <span style="color: #e0e2ea;">zig_install_dir</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">buildpath</span><span style="color: #e0e2ea;">/</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">zig</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="40">    <span style="color: #8cf8f7;">mkdir</span> <span style="color: #e0e2ea;">zig_install_dir</span>
</div><div class="line" data-line="41">    <span style="color: #e0e2ea;">resources</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">each</span> <span style="color: #e0e2ea; font-weight: bold;">do</span> <span style="color: #e0e2ea;">|</span><span style="color: #e0e2ea;">r</span><span style="color: #e0e2ea;">|</span>
</div><div class="line" data-line="42">      <span style="color: #e0e2ea;">r</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">fetch</span>
</div><div class="line" data-line="43">
</div><div class="line" data-line="44">      <span style="color: #8cf8f7;">system</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">tar</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">xvC</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">zig_install_dir</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">-f</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">r</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cached_download</span>
</div><div class="line" data-line="45">      <span style="color: #e0e2ea;">zig_dir</span> <span style="color: #e0e2ea;">=</span>
</div><div class="line" data-line="46">        <span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">Hardware</span><span style="color: #e0e2ea;">::</span><span style="color: #e0e2ea;">CPU</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">arm?</span>
</div><div class="line" data-line="47">          <span style="color: #e0e2ea;">zig_install_dir</span><span style="color: #e0e2ea;">/</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">zig-macos-aarch64-0.10.0</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="48">        <span style="color: #e0e2ea; font-weight: bold;">else</span>
</div><div class="line" data-line="49">          <span style="color: #e0e2ea;">zig_install_dir</span><span style="color: #e0e2ea;">/</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">zig-macos-x86_64-0.10.0</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="50">        <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="51">
</div><div class="line" data-line="52">      <span style="color: #e0e2ea;">ENV</span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">PATH</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">zig_dir</span><span style="color: #8cf8f7;">&rbrace;</span><span style="color: #b3f6c0;">:</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #e0e2ea;">+</span> <span style="color: #e0e2ea;">ENV</span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">PATH</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="53">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="54">
</div><div class="line" data-line="55">    <span style="color: #e0e2ea;">ENV</span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">PATH</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Formula</span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">python@3.9</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">opt_libexec</span><span style="color: #e0e2ea;">/</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">bin:</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">+</span> <span style="color: #e0e2ea;">ENV</span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">PATH</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="56">
</div><div class="line" data-line="57">    <span style="color: #8cf8f7;">system</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">mix</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">local.hex</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">--force</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="58">    <span style="color: #8cf8f7;">system</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">mix</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">local.rebar</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">--force</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="59">
</div><div class="line" data-line="60">    <span style="color: #e0e2ea;">ENV</span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">BURRITO_TARGET</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">Hardware</span><span style="color: #e0e2ea;">::</span><span style="color: #e0e2ea;">CPU</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">arm?</span>
</div><div class="line" data-line="61">      <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">macos_m1</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="62">    <span style="color: #e0e2ea; font-weight: bold;">else</span>
</div><div class="line" data-line="63">      <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">macos</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="64">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="65">
</div><div class="line" data-line="66">    <span style="color: #e0e2ea;">ENV</span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">MIX_ENV</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">prod</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="67">    <span style="color: #8cf8f7;">system</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">mix</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">deps.get</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="68">    <span style="color: #8cf8f7;">system</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">mix</span><span style="color: #b3f6c0;">&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">release</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="69">
</div><div class="line" data-line="70">    <span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">OS</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">mac?</span>
</div><div class="line" data-line="71">      <span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">Hardware</span><span style="color: #e0e2ea;">::</span><span style="color: #e0e2ea;">CPU</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">arm?</span>
</div><div class="line" data-line="72">        <span style="color: #e0e2ea;">bin</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">install</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">burrito_out/lazyasdf_macos_m1</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">lazyasdf</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="73">      <span style="color: #e0e2ea; font-weight: bold;">else</span>
</div><div class="line" data-line="74">        <span style="color: #e0e2ea;">bin</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">install</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">burrito_out/lazyasdf_macos</span><span style="color: #b3f6c0;">&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">lazyasdf</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="75">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="76">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="77">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="78">
</div><div class="line" data-line="79">  <span style="color: #8cf8f7;">test</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="80">    <span style="color: #9b9ea4;"># this is required for homebrew-core</span>
</div><div class="line" data-line="81">    <span style="color: #8cf8f7;">system</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">true</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="82">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="83"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>Here we can see all of <code>lazyasdf</code>'s dependencies.</p>
<p>It requires</p>
<ul>
<li>
<p>Elixir/Erlang: self-explanatory</p>
</li>
<li>
<p>asdf: self-explanatory</p>
</li>
<li>
<p>gcc, make: used to compile the termbox NIF bindings</p>
</li>
<li>
<p>Python 3.9: The termbox NIF uses a python script. For some reason it works with 3.9 and not 3.11, so I pinned it at 3.9 🤷‍♂️.</p>
</li>
<li>
<p>zig,xz: Burrito uses these two.</p>
<p>Burrito specifically uses Zig 0.10.0, not 0.10.1, so we have to specify it as a resource and download it from the Zig website. Luckily, they provide pre-compiled binaries for both of our target platforms, so we can just download, untar, and add them to our PATH!</p>
</li>
</ul>
<p>The Python dependency is even more quirky. The termbox scripts use the unversioned <code>python</code> executable, but Homebrew does not link those by default, so we have to manually add the unversioned one to our PATH for it to work.</p>
<p>Voilà!</p>
<h3><a href="#notes-1" aria-hidden="true" class="anchor" id="notes-1"></a>Notes</h3>
<p>Since this is a 3rd party Tap, the bottles that are generated are for an older version of Intel Mac, so those won't be very useful to anybody.</p>
<p>But if I were to merge this formula into homebrew-core, they would be bottled using the secret Homebrew GitHub Actions runners that can bottle it for all the OS's and architectures.</p>
<h2><a href="#the-future" aria-hidden="true" class="anchor" id="the-future"></a>The Future</h2>
<p>Ratatouille is incredible as it is today, but there is a lot of room for improvement.</p>
<p>As time allows, I hope to:</p>
<ul>
<li>Contribute to Ratatouille to allow more complex UI features like scrollbars and dynamic size information for elements.</li>
<li>Create bindings for termbox2 (the next iteration of termbox).</li>
<li>Create a higher level toolkit for building TUIs with Ratatouille, including menus, inputs, dialogs, etc.</li>
</ul>
<p>Thanks for reading!</p>
<hr />
<section class="footnotes" data-footnotes>
<ol>
<li id="fn-1">
<p>Previously, I have made a <a href="https://github.com/junegunn/fzf">fzf</a> clone using Ratatouille. You can find it in my <a href="https://github.com/mhanberg/.dotfiles/blob/ab07f27041780d1b54704ad4799382f58548468e/bin/fxf">dotfiles</a>. <a href="#fnref-1" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="1" aria-label="Back to reference 1">↩</a></p>
</li>
</ol>
</section> ]]></description>
    </item>
    <item>
       <title>The Comprehensive Guide to Elixir&#39;s List Comprehension</title>
       <link>https://www.mitchellhanberg.com/the-comprehensive-guide-to-elixirs-for-comprehension/</link>
       <pubDate>Thu, 02 Jun 2022 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/the-comprehensive-guide-to-elixirs-for-comprehension/</guid>
       <description><![CDATA[ <h2><a href="#what-is-it" aria-hidden="true" class="anchor" id="what-is-it"></a>What is it?</h2>
<p>The <code>for</code> special form, also known as a list comprehension, is a construct designed for concise and powerful enumerable transformation in Elixir.</p>
<p>It looks very similar to a "for loop" in other languages like JavaScript and C, but rather than being a language construct, it is an expression (like everything else in Elixir). This means that it evaluates to a value that can be bound to a variable. You may have heard this before as "statement vs expression".</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">last_names</span> <span style="color: #e0e2ea;">=</span> 
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">friend</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">friends</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="3">    <span style="color: #e0e2ea;">friend</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">last_name</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>Before reading this rest of this article, I suggest you read the list comprehension guide on <a href="https://elixir-lang.org/getting-started/comprehensions.html">elixir-lang.org</a>. I will go over some of the same details, but hopefully will go much more in depth.</p>
<h2><a href="#generators" aria-hidden="true" class="anchor" id="generators"></a>Generators</h2>
<p>The primary ingredient in a comprehension is the generator. The only other place you will see this "left arrow" syntax (<code>lhs &lt;- rhs</code>) is in the <code>with</code> special form.</p>
<p>The right-hand side is the enumerable you want to loop over and the left-hand side is the intermediate pattern you want to match on during each iteration. This is a normal pattern, so you can pattern match like you would anywhere else.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">friends</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">first_name: </span><span style="color: #b3f6c0;">&quot;Joe&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">last_name: </span><span style="color: #b3f6c0;">&quot;Swanson&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">first_name: </span><span style="color: #b3f6c0;">&quot;Greg&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">last_name: </span><span style="color: #b3f6c0;">&quot;Mefford&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">first_name: </span><span style="color: #b3f6c0;">&quot;Erik&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">last_name: </span><span style="color: #b3f6c0;">&quot;Plostins&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">last_name: </span><span style="color: #e0e2ea;">last_name</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">friends</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea;">last_name</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #9b9ea4;"># [&quot;Swanson&quot;, &quot;Mefford&quot;, &quot;Plostins&quot;]</span>
</div></code></pre>
<h3><a href="#multiple-generators" aria-hidden="true" class="anchor" id="multiple-generators"></a>Multiple Generators</h3>
<p>Comprehensions are very concise in that you can declare multiple generators, allowing you to generate every permutation of both enumerables. A great example is generating a list of coordinate pairs from a range of <code>x</code> values and <code>y</code> values.</p>
<p>A key detail to recognize is that the both values are yielded to the same block, so the result is a flat list.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">x</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">..</span><span style="color: #e0e2ea;">99</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">y</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">..</span><span style="color: #e0e2ea;">99</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">x</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">y</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #9b9ea4;"># [&lbrace;0, 0&rbrace;, &lbrace;0, 1&rbrace;, &lbrace;0, 2&rbrace;, ...]</span>
</div></code></pre>
<p>The counterexample using <a href="https://hexdocs.pm/elixir/Enum.html#map/2"><code>Enum.map/2</code></a> is not nearly as readable and demonstrates that you need to remember to flatten the outer loop, or else you'll get a list of lists.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">flat_map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">..</span><span style="color: #e0e2ea;">99</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">x</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">..</span><span style="color: #e0e2ea;">99</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">y</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="3">    <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">x</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">y</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #9b9ea4;"># [&lbrace;0, 0&rbrace;, &lbrace;0, 1&rbrace;, &lbrace;0, 2&rbrace;, ...]</span>
</div></code></pre>
<h3><a href="#generators-work-with-maps-too" aria-hidden="true" class="anchor" id="generators-work-with-maps-too"></a>Generators work with maps too</h3>
<p>The comprehension works with anything that implements the <code>Enumerable</code> protocol, so you can iterate through a map as well. The generator will yield each key/value pair of the given map.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">dictionary</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="2">  <span style="color: #b3f6c0;">&quot;low-latency&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #b3f6c0;">&quot;short delay before a transfer of data begins following an instruction for its transfer&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">  <span style="color: #b3f6c0;">&quot;distributed&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #b3f6c0;">&quot;(of a computer system) spread over several machines, especially over a network&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">  <span style="color: #b3f6c0;">&quot;fault-tolerant&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #b3f6c0;">&quot;relating to or being a computer or program with a self-contained backup system that allows continued operation when major components fail&quot;</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">word</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">definition</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">dictionary</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea;">IO</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">puts</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">word</span><span style="color: #8cf8f7;">&rbrace;</span>: <span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">definition</span><span style="color: #8cf8f7;">&rbrace;</span>&quot;</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>As seen above, we can iterate through a <code>Range</code> as well.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">x</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">..</span><span style="color: #e0e2ea;">99</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">y</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">..</span><span style="color: #e0e2ea;">99</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">x</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">y</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #9b9ea4;"># [&lbrace;0, 0&rbrace;, &lbrace;0, 1&rbrace;, &lbrace;0, 2&rbrace;, ...]</span>
</div></code></pre>
<h3><a href="#bitstring-generators" aria-hidden="true" class="anchor" id="bitstring-generators"></a>Bitstring generators</h3>
<p>The generators we've seen so far have been "list generators". The other type of generator is the "bitstring generator". The bitstring generator allows you to easily loop over a bitstring while correctly parsing bytes.</p>
<p>For a primer on bitstrings, please see the fantastic guide on <a href="https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html#bitstrings">elixir-lang.org</a>.</p>
<p>This is often very useful when parsing binary protocols like database protocols. Below is an example that demonstrates that each iteration of the comprehension reads the message length and then uses it to know how much more of the bitstring to read next. While the previous examples could be translated into various form of <code>Enum.map/2</code>, this example can only be achieved with normal recursion.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">bitstring</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lt;&lt;</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;I&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">6</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;really&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">4</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;love&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">4</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;list&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">14</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;comprehensions&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;!&quot;</span><span style="color: #e0e2ea;">&gt;&gt;</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #9b9ea4;"># &lt;&lt;1, 73, 6, 114, 101, 97, 108, 108, 121, 4, 108, 111, 118, 101, 4, 108, 105,</span>
</div><div class="line" data-line="4"><span style="color: #9b9ea4;">#  115, 116, 14, 99, 111, 109, 112, 114, 101, 104, 101, 110, 115, 105, 111, 110,</span>
</div><div class="line" data-line="5"><span style="color: #9b9ea4;">#  115, 1, 33&gt;&gt;</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">&lt;&lt;</span><span style="color: #e0e2ea;">message_length</span><span style="color: #e0e2ea;">::</span><span style="color: #e0e2ea;">integer</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">message</span><span style="color: #e0e2ea;">::</span><span style="color: #e0e2ea;">binary</span><span style="color: #e0e2ea;">-</span><span style="color: #8cf8f7;">size</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">message_length</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">bitstring</span><span style="color: #e0e2ea;">&gt;&gt;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea;">message</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #9b9ea4;"># [&quot;I&quot;, &quot;really&quot;, &quot;love&quot;, &quot;list&quot;, &quot;comprehensions&quot;, &quot;!&quot;]</span>
</div></code></pre>
<p>The example using recursion looks like:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">For</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">loop</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;&quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">loop</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lt;&lt;</span><span style="color: #e0e2ea;">message_length</span><span style="color: #e0e2ea;">::</span><span style="color: #e0e2ea;">integer</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">message</span><span style="color: #e0e2ea;">::</span><span style="color: #e0e2ea;">binary</span><span style="color: #e0e2ea;">-</span><span style="color: #8cf8f7;">size</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">message_length</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">rest</span><span style="color: #e0e2ea;">::</span><span style="color: #e0e2ea;">binary</span><span style="color: #e0e2ea;">&gt;&gt;</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">message</span> <span style="color: #e0e2ea;">|</span> <span style="color: #8cf8f7;">loop</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">rest</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9"><span style="color: #e0e2ea;">bitstring</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lt;&lt;</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;I&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">6</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;really&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">4</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;love&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">4</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;list&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">14</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;comprehensions&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;!&quot;</span><span style="color: #e0e2ea;">&gt;&gt;</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #e0e2ea;">For</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">loop</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">bitstring</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="12">
</div><div class="line" data-line="13"><span style="color: #9b9ea4;"># [&quot;I&quot;, &quot;really&quot;, &quot;love&quot;, &quot;list&quot;, &quot;comprehensions&quot;, &quot;!&quot;]</span>
</div></code></pre>
<p>To compare the two styles, let's look at the syntax of the generator in the comprehension example and the second function head of <code>loop</code> in the function example.</p>
<p>In the function example, you'll see that we pattern match on the bitstring in a similar manner to who you pattern match on a list. We pull several individual items off the beginning of the bitstring (<code>message_length::integer, message::binary-size(message_length)</code>) and we pattern match on the "rest" of the bitstring with <code>rest::binary</code>.</p>
<p>The list equivalent looks like:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">For</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">loop</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">loop</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">[</span><span style="color: #9b9ea4;">_length</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">message</span> <span style="color: #e0e2ea;">|</span> <span style="color: #e0e2ea;">rest</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">message</span> <span style="color: #e0e2ea;">|</span> <span style="color: #8cf8f7;">loop</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">rest</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9"><span style="color: #e0e2ea;">list</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;I&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">6</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;really&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">4</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;love&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">4</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;list&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">14</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;comprehensions&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;!&quot;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #e0e2ea;">For</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">loop</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">list</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="12">
</div><div class="line" data-line="13"><span style="color: #9b9ea4;"># [&quot;I&quot;, &quot;really&quot;, &quot;love&quot;, &quot;list&quot;, &quot;comprehensions&quot;, &quot;!&quot;]</span>
</div></code></pre>
<p>Now if we compare this to the bitstring generator, we can see the similarity (<em><strong>with a caveat</strong></em>). The <code>lhs</code> of the bitstring generator is the pattern match on the individual items and the <code>rhs</code> is pattern matching on the "rest". The caveat is that <code>bitstring</code> here is not actually the "rest" on each iteration, it is still the entire bitstring. I find this the best analogy to describe how the bitstring generator works since it is different enough from the "normal" list generator.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;">#   👇 individual item         👇 individual item                      👇rest</span>
</div><div class="line" data-line="2"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">&lt;&lt;</span><span style="color: #e0e2ea;">message_length</span><span style="color: #e0e2ea;">::</span><span style="color: #e0e2ea;">integer</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">message</span><span style="color: #e0e2ea;">::</span><span style="color: #e0e2ea;">binary</span><span style="color: #e0e2ea;">-</span><span style="color: #8cf8f7;">size</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">message_length</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">bitstring</span><span style="color: #e0e2ea;">&gt;&gt;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="3">  <span style="color: #9b9ea4;"># 👇 this will print the same thing every time</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea;">IO</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">inspect</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">bitstring</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6">  <span style="color: #e0e2ea;">message</span>
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<h3><a href="#chaining-generators" aria-hidden="true" class="anchor" id="chaining-generators"></a>Chaining generators</h3>
<p>List comprehensions allow you to chain generators together by using the <code>lhs</code> value from a generator in the <code>rhs</code> of a subsequent generator.</p>
<p>Here's an example that demonstrates getting a list of all of your friends hobbies:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">friends</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Derek&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;Movies&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;Hot Sauce&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Joe&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;Yu-Gi-Oh!&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;Tattoos&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Andres&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;Photoshop&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;Oreos&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;Cereal&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">hobbies</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">friends</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">hobby</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">hobbies</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea;">hobby</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #9b9ea4;"># [&quot;Movies&quot;, &quot;Hot Sauce&quot;, &quot;Yu-Gi-Oh!&quot;, &quot;Tattoos&quot;, &quot;Photoshop&quot;, &quot;Oreos&quot;, &quot;Cereal&quot;]</span>
</div></code></pre>
<h3><a href="#generators-filter-non-matching-lhs-values" aria-hidden="true" class="anchor" id="generators-filter-non-matching-lhs-values"></a>Generators filter non-matching lhs values</h3>
<p>If the match expression in the <code>lhs</code> of a generator does not match on the value yielded from the <code>rhs</code>, it will be rejected, and the list comprehension will move on to the next element in the enumerable.</p>
<p>This is slightly surprising behavior at first and should be kept in mind when using list comprehensions. The following example might lead to a bug in your program.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">friends</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">    <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #b3f6c0;">&quot;name&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #b3f6c0;">&quot;Derek&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Joe&quot;</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">friends</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="7">  <span style="color: #e0e2ea;">name</span>
</div><div class="line" data-line="8"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="9">
</div><div class="line" data-line="10"><span style="color: #9b9ea4;"># [&quot;Joe&quot;]</span>
</div></code></pre>
<p>If you were to have written this program using <code>Enum.map/2</code>, you would have ran into a function clause error.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">friends</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">    <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #b3f6c0;">&quot;name&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #b3f6c0;">&quot;Derek&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Joe&quot;</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6"><span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">friends</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="7">  <span style="color: #e0e2ea;">name</span>
</div><div class="line" data-line="8"><span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="9">
</div><div class="line" data-line="10"><span style="color: #9b9ea4;"># ** (FunctionClauseError) no function clause matching in :erl_eval.&quot;-inside-an-interpreted-fun-&quot;/1</span>
</div><div class="line" data-line="11"><span style="color: #9b9ea4;"># </span>
</div><div class="line" data-line="12"><span style="color: #9b9ea4;">#    The following arguments were given to :erl_eval.&quot;-inside-an-interpreted-fun-&quot;/1:</span>
</div><div class="line" data-line="13"><span style="color: #9b9ea4;"># </span>
</div><div class="line" data-line="14"><span style="color: #9b9ea4;">#        # 1</span>
</div><div class="line" data-line="15"><span style="color: #9b9ea4;">#        %&lbrace;&quot;name&quot; =&gt; &quot;Derek&quot;&rbrace;</span>
</div><div class="line" data-line="16"><span style="color: #9b9ea4;"># </span>
</div><div class="line" data-line="17"><span style="color: #9b9ea4;">#    (stdlib 3.17.1) :erl_eval.&quot;-inside-an-interpreted-fun-&quot;/1</span>
</div><div class="line" data-line="18"><span style="color: #9b9ea4;">#    (stdlib 3.17.1) erl_eval.erl:834: :erl_eval.eval_fun/6</span>
</div><div class="line" data-line="19"><span style="color: #9b9ea4;">#    (elixir 1.13.3) lib/enum.ex:1593: Enum.&quot;-map/2-lists^map/1-0-&quot;/2</span>
</div></code></pre>
<p>This behaviour can be useful! If you only wanted to iterate over configuration options that are enabled, you could write something like this:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">configs</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #8cf8f7;">:feature_a</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">enabled: </span><span style="color: #e0e2ea;">true</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #8cf8f7;">:feature_b</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">enabled: </span><span style="color: #e0e2ea;">false</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #8cf8f7;">:feature_c</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">enabled: </span><span style="color: #e0e2ea;">true</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">enabled: </span><span style="color: #e0e2ea;">true</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">config</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">configs</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea;">config</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #9b9ea4;"># [%&lbrace;name: :feature_a, enabled: true&rbrace;, %&lbrace;name: :feature_c, enabled: true&rbrace;]</span>
</div></code></pre>
<h2><a href="#filters" aria-hidden="true" class="anchor" id="filters"></a>Filters</h2>
<p>Now that we've talked about <em>generator filtering</em>, let's talk about <em>Filters</em>.</p>
<p>So far we have seen one type of "argument" that can be passed to the comprehension, the generator. Another "argument" is the filter! Let's look at a quick example.</p>
<p>In this example, we iterate over a list of employees, filter based on the employees status, and return a list of the employee's names</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">employees</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Eric&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Mitch&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:former</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Greg&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">employee</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">employees</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">status</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:active</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #9b9ea4;"># [&quot;Eric&quot;, &quot;Greg&quot;]</span>
</div></code></pre>
<p>As with generators, filters can use values bound in a previous step of the comprehension. And like generators, you can use multiple filters as well. You can even mix and match them!</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">employees</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Eric&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Text Adventures&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:gaming</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Chickens&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:animals</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="8">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Mitch&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="9">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:former</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="10">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Woodworking&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Homebrewing&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="11">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="12">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="13">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Greg&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="14">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="15">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="16">      <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Dungeons &amp; Dragons&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:gaming</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="17">      <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Woodworking&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="18">    <span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="19">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="20"><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="21">
</div><div class="line" data-line="22"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">employee</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">employees</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="23">    <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">status</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="24">    <span style="color: #e0e2ea;">hobby</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">hobbies</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="25">    <span style="color: #e0e2ea;">hobby</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">type</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:gaming</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="26">  <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">hobby</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="27"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="28">
</div><div class="line" data-line="29"><span style="color: #9b9ea4;"># [</span>
</div><div class="line" data-line="30"><span style="color: #9b9ea4;">#   &lbrace;&quot;Eric&quot;, %&lbrace;name: &quot;Text Adventures&quot;, type: :gaming&rbrace;&rbrace;,</span>
</div><div class="line" data-line="31"><span style="color: #9b9ea4;">#   &lbrace;&quot;Greg&quot;, %&lbrace;name: &quot;Dungeons &amp; Dragons&quot;, type: :gaming&rbrace;&rbrace;</span>
</div><div class="line" data-line="32"><span style="color: #9b9ea4;"># ]</span>
</div><div class="line" data-line="33">
</div></code></pre>
<p>At this point we can recognize that the list comprehension has the characteristics of a function with <a href="https://en.wikipedia.org/wiki/Variadic_function">variadic arguments</a>. If we were to write our own <code>for</code> using plain functions, we'd have to pass it a list of callbacks to evaluate and a final callback to do the mapping. While we aren't necessarily concerned with how we'd implement <code>for</code> as a plain function, it's important to recognize aspects that are "different" from "normal" constructs in the language.</p>
<p>One of the great things about the list comprehension is that it allows you to operate on <code>Enumerable</code> data structures in fewer passes (usually 1) than when using the <code>Enum</code> module.</p>
<p>We can write our previous example using functions like so:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">employees</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Eric&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Mitch&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:former</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Greg&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #e0e2ea;">employees</span>
</div><div class="line" data-line="8"><span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">filter</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">employee</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">status</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:active</span> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">employee</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #9b9ea4;"># [&quot;Eric&quot;, &quot;Greg&quot;]</span>
</div><div class="line" data-line="12">
</div><div class="line" data-line="13"><span style="color: #e0e2ea;">employees</span>
</div><div class="line" data-line="14"><span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">reduce</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">acc</span> <span style="color: #e0e2ea;">-&gt;</span> 
</div><div class="line" data-line="15">  <span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">status</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:active</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="16">    <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span> <span style="color: #e0e2ea;">|</span> <span style="color: #e0e2ea;">acc</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="17">  <span style="color: #e0e2ea; font-weight: bold;">else</span>
</div><div class="line" data-line="18">    <span style="color: #e0e2ea;">acc</span>
</div><div class="line" data-line="19">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="20"><span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="21"><span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">reverse</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="22">
</div><div class="line" data-line="23"><span style="color: #9b9ea4;"># [&quot;Eric&quot;, &quot;Greg&quot;]</span>
</div><div class="line" data-line="24">
</div><div class="line" data-line="25"><span style="color: #e0e2ea;">:lists</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">filtermap</span><span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="26">  <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">employee</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="27">    <span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">status</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:active</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="28">      <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">true</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="29">    <span style="color: #e0e2ea; font-weight: bold;">else</span>
</div><div class="line" data-line="30">      <span style="color: #e0e2ea;">false</span>
</div><div class="line" data-line="31">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="32">  <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="33">  <span style="color: #e0e2ea;">employees</span>
</div><div class="line" data-line="34"><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="35">
</div><div class="line" data-line="36"><span style="color: #9b9ea4;"># [&quot;Eric&quot;, &quot;Greg&quot;]</span>
</div></code></pre>
<p>You can see benchmarks of all of these styles of "filter map" <a href="https://github.com/mhanberg/notebooks/blob/c0c0c710fd5ca7a6f36d5acdca9043911d49044d/notebooks/list_benchmarks.livemd">here</a>.</p>
<h2><a href="#options" aria-hidden="true" class="anchor" id="options"></a>Options</h2>
<p>Now that we've covered the basic principles of the list comprehension, we can explore the various options that can be passed to augment it's behavior. The default behavior is to act more or less like <code>Enum.map/2</code> with regard to the return type.</p>
<p>As of this writing, there are three options available: <code>:uniq</code>, <code>:into</code>, and <code>:reduce</code></p>
<h3><a href="#uniq" aria-hidden="true" class="anchor" id="uniq"></a>:uniq</h3>
<p><code>:uniq</code> is the least interesting of the available options, but still quite powerful.</p>
<p>It simply ensures that the return result will only contain unique values.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">employees</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Eric&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Text Adventures&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:gaming</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Chickens&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:animals</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="8">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Mitch&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="9">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:former</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="10">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Woodworking&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Homebrewing&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="11">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="12">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="13">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Greg&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="14">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="15">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="16">      <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Dungeons &amp; Dragons&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:gaming</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="17">      <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Woodworking&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="18">    <span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="19">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="20"><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="21">
</div><div class="line" data-line="22"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">employee</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">employees</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">hobby</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">hobbies</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">uniq: </span><span style="color: #e0e2ea;">true</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="23">  <span style="color: #e0e2ea;">hobby</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span>
</div><div class="line" data-line="24"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="25">
</div><div class="line" data-line="26"><span style="color: #9b9ea4;"># [&quot;Text Adventures&quot;, &quot;Chickens&quot;, &quot;Woodworking&quot;, &quot;Homebrewing&quot;, &quot;Dungeons &amp; Dragons&quot;]</span>
</div></code></pre>
<p>You can see benchmarks of all of these styles of "map uniq" <a href="https://github.com/mhanberg/notebooks/blob/ae5362fded45465e22e74b9d310715e7852502d8/notebooks/list_benchmarks.livemd#results-mapuniq">here</a>.</p>
<h3><a href="#into" aria-hidden="true" class="anchor" id="into"></a>:into</h3>
<p><code>:into</code> is where things start to get interesting.</p>
<p>The default behavior for a list comprehension behaves more or less like a "map" operation, meaning that the expression evaluates to a list.</p>
<p>The <code>:into</code> option allows you to instead push the value returned by each iteration into a <em>collectable</em>. A data structure is collectable if it implements the <a href="https://hexdocs.pm/elixir/Collectable.html">Collectable</a> protocol.</p>
<p>If you aren't familiar with protocols, you have already been using them! The <code>Enum</code> module is a set of functions that operate on data structures that implement the <a href="https://hexdocs.pm/elixir/Enumerable.html">Enumerable</a> protocol. The builtin data structures that implement the <code>Enumerable</code> protocol are the <code>List</code>, <code>Range</code>, <code>Map</code>, and <code>MapSet</code> types.</p>
<p>The builtin data structures that implement the <code>Collectable</code> protocol are <code>List</code>, <code>Map</code>, <code>MapSet</code>, and <code>BitString</code>. The <code>Enum</code> function that you would use to take advantage of this protocol is <a href="https://hexdocs.pm/elixir/Enum.html#into/3">Enum.into/2</a>.</p>
<p>Let's take a look at some examples.</p>
<h4><a href="#list" aria-hidden="true" class="anchor" id="list"></a>List</h4>
<p>Using a list as the <code>:into</code> actually doesn't change the behavior at all!</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">employees</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Eric&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Text Adventures&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:gaming</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Chickens&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:animals</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="8">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Mitch&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="9">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:former</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="10">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Woodworking&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Homebrewing&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="11">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="12">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="13">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Greg&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="14">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="15">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="16">      <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Dungeons &amp; Dragons&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:gaming</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="17">      <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Woodworking&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="18">    <span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="19">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="20"><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="21">
</div><div class="line" data-line="22"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">employee</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">employees</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="23">    <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">status</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="24">    <span style="color: #e0e2ea;">hobby</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">hobbies</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="25">    <span style="color: #e0e2ea;">hobby</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">type</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:gaming</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="26">    <span style="color: #8cf8f7;">into: </span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="27">  <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">hobby</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="28"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="29">
</div><div class="line" data-line="30"><span style="color: #9b9ea4;"># [</span>
</div><div class="line" data-line="31"><span style="color: #9b9ea4;">#   &lbrace;&quot;Eric&quot;, %&lbrace;name: &quot;Text Adventures&quot;, type: :gaming&rbrace;&rbrace;,</span>
</div><div class="line" data-line="32"><span style="color: #9b9ea4;">#   &lbrace;&quot;Greg&quot;, %&lbrace;name: &quot;Dungeons &amp; Dragons&quot;, type: :gaming&rbrace;&rbrace;</span>
</div><div class="line" data-line="33"><span style="color: #9b9ea4;"># ]</span>
</div></code></pre>
<h4><a href="#map" aria-hidden="true" class="anchor" id="map"></a>Map</h4>
<p>But if we use a map, we can see that it pushes each key/value pair into the map that you pass for the option. Usually I use this with an empty map (<code>%&lbrace;&rbrace;</code> or <code>Map.new()</code>), but let's look at an example using a non-empty map.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">employees</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Eric&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Text Adventures&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:gaming</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Chickens&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:animals</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="8">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Mitch&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="9">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:former</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="10">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Woodworking&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Homebrewing&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="11">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="12">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="13">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Greg&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="14">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="15">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="16">      <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Dungeons &amp; Dragons&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:gaming</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="17">      <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Woodworking&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="18">    <span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="19">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="20"><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="21">
</div><div class="line" data-line="22"><span style="color: #e0e2ea;">base_map</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="23">  <span style="color: #b3f6c0;">&quot;Mitch&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="24">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Reading&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="25">    <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:learning</span>
</div><div class="line" data-line="26">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="27">  <span style="color: #b3f6c0;">&quot;Greg&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="28">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Traveling&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="29">    <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:expensive</span>
</div><div class="line" data-line="30">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="31"><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="32">
</div><div class="line" data-line="33"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">employee</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">employees</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="34">    <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">status</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="35">    <span style="color: #e0e2ea;">hobby</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">hobbies</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="36">    <span style="color: #e0e2ea;">hobby</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">type</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:gaming</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="37">    <span style="color: #8cf8f7;">into: </span><span style="color: #e0e2ea;">base_map</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="38">  <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">hobby</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="39"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="40">
</div><div class="line" data-line="41"><span style="color: #9b9ea4;"># %&lbrace;</span>
</div><div class="line" data-line="42"><span style="color: #9b9ea4;">#   &quot;Eric&quot; =&gt; %&lbrace;name: &quot;Text Adventures&quot;, type: :gaming&rbrace;,</span>
</div><div class="line" data-line="43"><span style="color: #9b9ea4;">#   &quot;Greg&quot; =&gt; %&lbrace;name: &quot;Dungeons &amp; Dragons&quot;, type: :gaming&rbrace;,</span>
</div><div class="line" data-line="44"><span style="color: #9b9ea4;">#   &quot;Mitch&quot; =&gt; %&lbrace;name: &quot;Reading&quot;, type: :learning&rbrace;</span>
</div><div class="line" data-line="45"><span style="color: #9b9ea4;"># &rbrace;</span>
</div></code></pre>
<p>Here we can observe three things.</p>
<ul>
<li>The comprehension evaluates to a map.</li>
<li>The <code>&quot;Mitch&quot;</code> key and its value were preserved in the final output.</li>
<li>The <code>&quot;Greg&quot;</code> key's value in the <code>base_map</code> was overwritten by the value yielded during the comprehension with the same key. If our comprehension were to have returned multiple key/value pairs with identical keys, the last one would have won.</li>
</ul>
<p>This option is very useful for transforming maps. Since iterating over a map with an <code>Enum</code> function turns it into a list of 2-tuples, you always need to pipe the return value into <code>Enum.into/2</code> or <code>Map.new/1</code>.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">employees</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Eric&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Text Adventures&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:gaming</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Chickens&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:animals</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="8">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Mitch&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="9">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:former</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="10">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Woodworking&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Homebrewing&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="11">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="12">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="13">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Greg&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="14">    <span style="color: #8cf8f7;">status: </span><span style="color: #8cf8f7;">:active</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="15">    <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="16">      <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Dungeons &amp; Dragons&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:gaming</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="17">      <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Woodworking&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:making</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="18">    <span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="19">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="20"><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="21">
</div><div class="line" data-line="22"><span style="color: #e0e2ea;">base_map</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="23">  <span style="color: #b3f6c0;">&quot;Mitch&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="24">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Reading&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="25">    <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:learning</span>
</div><div class="line" data-line="26">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="27">  <span style="color: #b3f6c0;">&quot;Greg&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="28">    <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Traveling&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="29">    <span style="color: #8cf8f7;">type: </span><span style="color: #8cf8f7;">:expensive</span>
</div><div class="line" data-line="30">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="31"><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="32">
</div><div class="line" data-line="33"><span style="color: #e0e2ea;">employees</span>
</div><div class="line" data-line="34"><span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">filter</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">employee</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">status</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:active</span> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="35"><span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">flat_map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">employee</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="36">  <span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">hobbies</span>
</div><div class="line" data-line="37">  <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">filter</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">hobby</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">hobby</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">type</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:gaming</span> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="38">  <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">hobby</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="39">    <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">employee</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">hobby</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="40">  <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="41"><span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="42"><span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">into</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">base_map</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="43">
</div><div class="line" data-line="44"><span style="color: #9b9ea4;"># %&lbrace;</span>
</div><div class="line" data-line="45"><span style="color: #9b9ea4;">#   &quot;Eric&quot; =&gt; %&lbrace;name: &quot;Text Adventures&quot;, type: :gaming&rbrace;,</span>
</div><div class="line" data-line="46"><span style="color: #9b9ea4;">#   &quot;Greg&quot; =&gt; %&lbrace;name: &quot;Dungeons &amp; Dragons&quot;, type: :gaming&rbrace;,</span>
</div><div class="line" data-line="47"><span style="color: #9b9ea4;">#   &quot;Mitch&quot; =&gt; %&lbrace;name: &quot;Reading&quot;, type: :learning&rbrace;</span>
</div><div class="line" data-line="48"><span style="color: #9b9ea4;"># &rbrace;</span>
</div></code></pre>
<h4><a href="#strings-and-bitstrings" aria-hidden="true" class="anchor" id="strings-and-bitstrings"></a>Strings and BitStrings</h4>
<p>You can build strings and bitstrings with the <code>:into</code> option as well!</p>
<p>This is useful when you want to build a string or a binary out of a list or map all in one pass. Let's take a look at an example of creating an "attribute string" for use with HTML.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">attributes</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;font-bold text-red-500 underline&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">  <span style="color: #8cf8f7;">id: </span><span style="color: #b3f6c0;">&quot;error-text&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">data_controller: </span><span style="color: #b3f6c0;">&quot;error-controller&quot;</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">property</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">value</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">attributes</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">into: </span><span style="color: #b3f6c0;">&quot;&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea;">property</span> <span style="color: #e0e2ea;">=</span>
</div><div class="line" data-line="9">    <span style="color: #e0e2ea;">property</span>
</div><div class="line" data-line="10">    <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #8cf8f7;">to_string</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="11">    <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">String</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">replace</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;_&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;-&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="12">
</div><div class="line" data-line="13">  <span style="color: #b3f6c0;">~</span>s| <span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">property</span><span style="color: #8cf8f7;">&rbrace;</span>=&quot;<span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">value</span><span style="color: #8cf8f7;">&rbrace;</span>&quot;|
</div><div class="line" data-line="14"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="15">
</div><div class="line" data-line="16"><span style="color: #9b9ea4;"># &quot; class=\&quot;font-bold text-red-500 underline\&quot; id=\&quot;error-text\&quot; data-controller=\&quot;error-controller\&quot;&quot;</span>
</div></code></pre>
<h3><a href="#reduce" aria-hidden="true" class="anchor" id="reduce"></a>:reduce</h3>
<p>My favorite option to the comprehension is <code>:reduce</code>!</p>
<p>Reduce allows us to change the comprehension from behaving like a "map" operation to a "reduce" operation. This means that it will loop over an enumerable, but collect an "accumulator" instead.</p>
<p>Let's take a look at the first example in the <code>Enum.reduce/3</code> documentation and then convert it to a comprehension. This example produces the sum of a list of integers.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">reduce</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">2</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">3</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">x</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">acc</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">x</span> <span style="color: #e0e2ea;">+</span> <span style="color: #e0e2ea;">acc</span>
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #9b9ea4;"># 6</span>
</div></code></pre>
<p>We can express this as a comprehension like so:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">x</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">2</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">3</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">reduce: </span><span style="color: #e0e2ea;">0</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">acc</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="3">    <span style="color: #e0e2ea;">x</span> <span style="color: #e0e2ea;">+</span> <span style="color: #e0e2ea;">acc</span>
</div><div class="line" data-line="4"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6"><span style="color: #9b9ea4;"># 6</span>
</div></code></pre>
<p>There are two immediate things we can observe.</p>
<p>First, the <code>:reduce</code> option takes a value that is to be used as the first value of the accumulator.</p>
<p>Second, the comprehension in this mode includes a slightly different syntax. Here the inside of the block includes the "arg(s) and right arrow" syntax that you see in anonymous functions and case expressions. This is the syntax that allows the comprehensions to yield the accumulator to the block on every iteration.</p>
<p>The additional syntax is the same as the other places you have probably seen it; you can pattern match and pass additional clauses!</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">directions</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">left: </span><span style="color: #e0e2ea;">2</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">  <span style="color: #8cf8f7;">up: </span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">down: </span><span style="color: #e0e2ea;">5</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5">  <span style="color: #8cf8f7;">right: </span><span style="color: #e0e2ea;">6</span>
</div><div class="line" data-line="6"><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8"><span style="color: #9b9ea4;"># You can&#39;t move below or to the left of 0.</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea;">starting_position</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">dir</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">movement</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">directions</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">reduce: </span><span style="color: #e0e2ea;">starting_position</span>  <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="12">  <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">x</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">y</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea; font-weight: bold;">when</span> <span style="color: #e0e2ea;">dir</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:left</span> <span style="color: #e0e2ea; font-weight: bold;">and</span> <span style="color: #e0e2ea;">x</span> <span style="color: #e0e2ea;">-</span> <span style="color: #e0e2ea;">movement</span> <span style="color: #e0e2ea;">&gt;</span> <span style="color: #e0e2ea;">0</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="13">    <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">x</span> <span style="color: #e0e2ea;">-</span> <span style="color: #e0e2ea;">movement</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">y</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="14">
</div><div class="line" data-line="15">  <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">x</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">y</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea; font-weight: bold;">when</span> <span style="color: #e0e2ea;">dir</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:down</span> <span style="color: #e0e2ea; font-weight: bold;">and</span> <span style="color: #e0e2ea;">y</span> <span style="color: #e0e2ea;">-</span> <span style="color: #e0e2ea;">movement</span> <span style="color: #e0e2ea;">&gt;</span> <span style="color: #e0e2ea;">0</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="16">    <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">x</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">y</span> <span style="color: #e0e2ea;">-</span> <span style="color: #e0e2ea;">movement</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="17">
</div><div class="line" data-line="18">  <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">x</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">y</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea; font-weight: bold;">when</span> <span style="color: #e0e2ea;">dir</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:up</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="19">    <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">x</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">y</span> <span style="color: #e0e2ea;">+</span> <span style="color: #e0e2ea;">movement</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="20">
</div><div class="line" data-line="21">  <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">x</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">y</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea; font-weight: bold;">when</span> <span style="color: #e0e2ea;">dir</span> <span style="color: #e0e2ea;">==</span> <span style="color: #8cf8f7;">:right</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="22">    <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">x</span> <span style="color: #e0e2ea;">+</span> <span style="color: #e0e2ea;">movement</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">y</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="23">
</div><div class="line" data-line="24">  <span style="color: #e0e2ea;">position</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="25">    <span style="color: #e0e2ea;">IO</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">puts</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;Not possible to move <span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">dir</span><span style="color: #8cf8f7;">&rbrace;</span> by <span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">movement</span><span style="color: #8cf8f7;">&rbrace;</span> when you care located at <span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #8cf8f7;">inspect</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">position</span><span style="color: #e0e2ea;">)</span><span style="color: #8cf8f7;">&rbrace;</span>&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="26">
</div><div class="line" data-line="27">    <span style="color: #e0e2ea;">position</span>
</div><div class="line" data-line="28"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="29">
</div><div class="line" data-line="30"><span style="color: #9b9ea4;"># &lbrace;6, 1&rbrace;</span>
</div></code></pre>
<p>Above we can observe that we've written 5 different clauses, pattern matched on the shape of the data, as well as added guard clauses that capture the data in the generator and the accumulator.</p>
<p>The beauty of using a comprehension as a reducer is the ability to use multiple generators and act on them as if they are one level of iteration.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">friends</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Derek&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;Movies&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;Hot Sauce&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Joe&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;Yu-Gi-Oh!&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;Tattoos&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;Andres&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;Photoshop&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;Oreos&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;Cereal&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">hobbies</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">name: </span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">friends</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">hobby</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">hobbies</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">reduce: </span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea;">tagged_hobbies</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="9">    <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">hobby</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">|</span> <span style="color: #e0e2ea;">tagged_hobbies</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="10"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="11">
</div><div class="line" data-line="12"><span style="color: #9b9ea4;"># [</span>
</div><div class="line" data-line="13"><span style="color: #9b9ea4;">#   &lbrace;&quot;Andres&quot;, &quot;Cereal&quot;&rbrace;,</span>
</div><div class="line" data-line="14"><span style="color: #9b9ea4;">#   &lbrace;&quot;Andres&quot;, &quot;Oreos&quot;&rbrace;,</span>
</div><div class="line" data-line="15"><span style="color: #9b9ea4;">#   &lbrace;&quot;Andres&quot;, &quot;Photoshop&quot;&rbrace;,</span>
</div><div class="line" data-line="16"><span style="color: #9b9ea4;">#   &lbrace;&quot;Joe&quot;, &quot;Tattoos&quot;&rbrace;,</span>
</div><div class="line" data-line="17"><span style="color: #9b9ea4;">#   &lbrace;&quot;Joe&quot;, &quot;Yu-Gi-Oh!&quot;&rbrace;,</span>
</div><div class="line" data-line="18"><span style="color: #9b9ea4;">#   &lbrace;&quot;Derek&quot;, &quot;Hot Sauce&quot;&rbrace;,</span>
</div><div class="line" data-line="19"><span style="color: #9b9ea4;">#   &lbrace;&quot;Derek&quot;, &quot;Movies&quot;&rbrace;</span>
</div><div class="line" data-line="20"><span style="color: #9b9ea4;"># ]</span>
</div></code></pre>
<p>To write this without a comprehension it would look something like:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">reduce</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">friends</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">hobbies: </span><span style="color: #e0e2ea;">hobbies</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">tagged_hobbies</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">new_hobbies</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">hobbies</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">hobby</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">hobby</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea;">new_hobbies</span> <span style="color: #e0e2ea;">++</span> <span style="color: #e0e2ea;">tagged_hobbies</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<h2><a href="#conclusion" aria-hidden="true" class="anchor" id="conclusion"></a>Conclusion</h2>
<p>If you've made it this far, congrats! The comprehension packs a lot of features into a tiny programming construct and demonstrating all of them is a lot of work!</p>
<p>The comprehension is one of my favorite features of the Elixir programming language, and it was a pleasure to write about every feature in as much depth as I could.</p>
<p>If you have any questions about comprehensions or want to suggest examples or features that I've missed, feel free to reach out on <a href="https://twitter.com/mitchhanberg">Twitter</a> or <a href="mailto:contact@mitchellhanberg.com">email</a>.</p>
<h2><a href="#supplementary-information" aria-hidden="true" class="anchor" id="supplementary-information"></a>Supplementary Information</h2>
<h3><a href="#list-comprehensions-in-erlang" aria-hidden="true" class="anchor" id="list-comprehensions-in-erlang"></a>List Comprehensions in Erlang</h3>
<p>It would be remiss to not mention that the list comprehension also exists in Erlang. I am not personally familiar with them, so I won't explain them very much, but I I'll provide an example and a link to learn more about them on your own.</p>
<p>The following examples are from the official Erlang/OTP documentation and can be found <a href="https://erlang.org/doc/programming_examples/list_comprehensions.html">here</a>.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-erlang" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">X</span> || <span style="color: #e0e2ea;">X</span> &lt;- <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">2</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">a</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">3</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">4</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">5</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">6</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">is_integer</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">X</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">X</span> <span style="color: #e0e2ea;">&gt;</span> <span style="color: #e0e2ea;">3</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">.</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #9b9ea4;">% [4, 5, 6]</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">X</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">Y</span><span style="color: #e0e2ea;">&rbrace;</span> || <span style="color: #e0e2ea;">X</span> &lt;- <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">2</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">3</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span>  <span style="color: #e0e2ea;">Y</span> &lt;- <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">a</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">.</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #9b9ea4;">% [&lbrace;1, a&rbrace;, &lbrace;1, b&rbrace;, &lbrace;2, a&rbrace;, &lbrace;2, b&rbrace;, &lbrace;3, a&rbrace;, &lbrace;3, b&rbrace;]</span>
</div></code></pre> ]]></description>
    </item>
    <item>
       <title>How EEx Turns Your Template Into HTML</title>
       <link>https://www.mitchellhanberg.com/how-eex-turns-your-template-into-html/</link>
       <pubDate>Mon, 11 Apr 2022 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/how-eex-turns-your-template-into-html/</guid>
       <description><![CDATA[ <p>EEx is a templating language and module built into the <a href="https://hexdocs.pm/eex/EEx.html">Elixir</a> standard library, often used by web servers to render dynamic content for a web application. The same concept exists in other language ecosystems, ERB for Ruby, Blade for Laravel/PHP, Pug/Jade for JavaScript.</p>
<p>A couple features of EEx that make it stand out are:</p>
<ul>
<li>Templates can be compiled into functions ahead of time, avoiding needing to read from the filesystem at runtime.</li>
<li>EEx has the concept of an "Engine" allowing you to compile an existing template into something different than what the builtin engine, <a href="https://hexdocs.pm/eex/EEx.SmartEngine.html"><code>EEx.SmartEngine</code></a>, does. This is what <a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html">Phoenix LiveView</a> does.</li>
</ul>
<p>Recently, <a href="https://twitter.com/josevalim/status/1511348149323014155?s=20&amp;t=sFyaicmJApbs5yQO-n_CIA">José Valim tweeted me some advice on how to use the EEx Engine</a> in my library <a href="https://github.com/mhanberg/temple">Temple</a> (an alternative HTML library, that uses an Elixir DSL). At first I was very confused at José's suggestion; I don't want to write EEx at all! I want Temple to go from DSL straight to <code>iolist</code> or <code>%LiveView.Rendered&lbrace;&rbrace;</code> structs.</p>
<p>Luckily, my coworker <a href="https://twitter.com/MarlusSaraiva">Marlus Saraiva</a> messaged me and gave a bit more context on José's suggestion, and suddenly it all clicked!. Hopefully at the end of this post, you'll understand how José's suggestion makes perfect sense and was extremely clear.</p>
<p>This conversation led me to ask myself, how does EEx work anyway? I decided to source dive EEx, LiveView, <a href="https://surface-ui.org/">Surface</a>, and <a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.Helpers.html#sigil_H/2-syntax">HEEx</a> and will share with you what I learned!</p>
<h2><a href="#the-source-code" aria-hidden="true" class="anchor" id="the-source-code"></a>The Source Code</h2>
<p>Technically EEx is just a templating syntax that can handle any plaintext format. It doesn't matter if its HTML, JavaScript, or even Elixir (this is how the Phoenix generators work).</p>
<p>EEx templates allow you to execute arbitrary Elixir code inside of special tags and inject the runtime result into the return value. You can use single expressions or even expressions that take blocks. The <code>EEx.SmartEngine</code> also allows you to ergonomically access data passed to the template with the <code>@</code> syntax.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-eex" translate="no" tabindex="0"><div class="line" data-line="1">&lt;!-- single line expression --&gt;
</div><div class="line" data-line="2">&lt;span&gt;<span style="color: #8cf8f7;">&lt;%=</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">user</span></span></span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span><span style="color: #8cf8f7;"> %&gt;</span>&lt;/span&gt;
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">&lt;!-- block expression --&gt;
</div><div class="line" data-line="5">&lt;ul&gt;
</div><div class="line" data-line="6">  <span style="color: #8cf8f7;">&lt;%=</span> <span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">user</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">users do <span style="color: #8cf8f7;">%&gt;</span></span></span></span></span>
</div><div class="line" data-line="7"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;"><span style="color: #8cf8f7;"><span style="color: #e0e2ea;">    &lt;li&gt;<span style="color: #8cf8f7;">&lt;%=</span> <span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span><span style="color: #8cf8f7;"> %&gt;</span>&lt;/li&gt;</span></span></span></span>
</div><div class="line" data-line="8"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;"><span style="color: #8cf8f7;"><span style="color: #e0e2ea;">  <span style="color: #8cf8f7;">&lt;%</span> <span style="color: #e0e2ea;">end</span></span></span></span></span><span style="color: #8cf8f7;"> %&gt;</span>
</div><div class="line" data-line="9">&lt;/ul&gt;
</div></code></pre>
<h2><a href="#the-compiler" aria-hidden="true" class="anchor" id="the-compiler"></a>The Compiler</h2>
<p>The public API for compiling an EEx file or string consists of the <code>EEx.compile_file/2</code> and <code>EEx.compile_string/2</code> functions. Internally, they will call into the <code>EEx.Compiler</code> module.</p>
<p>The <code>EEx.Compiler</code> module is where the coordination happens when compiling our source code. You can find the source code <a href="https://github.com/elixir-lang/elixir/blob/v1.13.3/lib/eex/lib/eex/compiler.ex">here</a>.</p>
<p>This module's <code>compile/2</code> function is responsible for calling the tokenizer, traversing them, and passing them to the appropriate callbacks on the engine. By default, EEx uses the builtin <code>EEx.SmartEngine</code>.</p>
<p>Essentially there are two steps:</p>
<ol>
<li>Turn the source into tokens.</li>
<li>Turn the tokens into Elixir AST by passing them to the relevant callback on the given <code>EEx.Engine</code>.</li>
</ol>
<h2><a href="#the-tokenizer" aria-hidden="true" class="anchor" id="the-tokenizer"></a>The Tokenizer</h2>
<p>The first thing that the compiler will do is tokenize the content. You can find the source code <a href="https://github.com/elixir-lang/elixir/blob/v1.13.3/lib/eex/lib/eex/tokenizer.ex">here</a>.</p>
<p>This translates the source code into a list of Elixir terms that describe the tokens found in the document. The above example will be tokenized into the following.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="2"> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="3">   <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:text</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&#39;&lt;!-- single line expression --&gt;<span style="color: #8cf8f7;">\n</span>&lt;span&gt;&#39;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">   <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:expr</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">7</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&#39;=&#39;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&#39; @user.name &#39;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5">   <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:text</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">24</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&#39;&lt;/span&gt;<span style="color: #8cf8f7;">\n</span><span style="color: #8cf8f7;">\n</span>&lt;!-- block expression --&gt;<span style="color: #8cf8f7;">\n</span>&lt;ul&gt;<span style="color: #8cf8f7;">\n</span>&#39;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="6">   <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:start_expr</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">5</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">3</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&#39;=&#39;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&#39; for user &lt;- @users do &#39;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">   <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:text</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">5</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">31</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&#39;<span style="color: #8cf8f7;">\n</span>    &lt;li&gt;&#39;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="8">   <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:expr</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">6</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">9</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&#39;=&#39;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&#39; user.name &#39;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="9">   <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:text</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">6</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">25</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&#39;&lt;/li&gt;<span style="color: #8cf8f7;">\n</span>&#39;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="10">   <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:end_expr</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">7</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">3</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&#39; end &#39;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="11">   <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:text</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">7</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">12</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&#39;<span style="color: #8cf8f7;">\n</span>&lt;/ul&gt;<span style="color: #8cf8f7;">\n</span>&#39;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="12">   <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:eof</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">9</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="13"> <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<p>The list of tokens are fairly self explanatory. We can see <code>:text</code> tokens for static ranges of plain text, <code>:expr</code> tokens for single line expressions, and <code>:start_expr</code>/<code>:end_expr</code> tokens for the multi-line do/end block expression.</p>
<p>The tokens also include some row/column metadata, which is helpful later on for attributing error messages to the origin point in the source code.</p>
<p>Now that we have the tokens, we need to traverse over them and pass them into the engine.</p>
<h2><a href="#the-engine" aria-hidden="true" class="anchor" id="the-engine"></a>The Engine</h2>
<p>The engine is where the magic happens! It's responsible for building the tokens into usable Elixir AST, which can then be injected into a function.</p>
<p>An <code>EEx.Engine</code> has the following callbacks that it must implement and that your compiler must call appropriately. I'll copy/paste some of the callback documentation here, copyright belonging to the elixir-lang/elixir project.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;">@<span style="color: #9b9ea4;">doc</span> <span style="color: #b3f6c0;">&quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="2"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">Called at the beginning of every template.</span></span></span>
</div><div class="line" data-line="3"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">It must return the initial state.</span></span></span>
</div><div class="line" data-line="4"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">&quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">callback <span style="color: #8cf8f7;">init</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">opts</span> <span style="color: #e0e2ea;">::</span> <span style="color: #e0e2ea;">keyword</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">::</span> <span style="color: #e0e2ea;">state</span></span></span></span></span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;">@<span style="color: #9b9ea4;">doc</span> <span style="color: #b3f6c0;">&quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="8"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">Called at the end of every template.</span></span></span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">It must return Elixir&#39;s quoted expressions for the template.</span></span></span>
</div><div class="line" data-line="10"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">&quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="11"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">callback <span style="color: #8cf8f7;">handle_body</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">::</span> <span style="color: #e0e2ea;">Macro</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">t</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span></span></span></span></span>
</div><div class="line" data-line="12">
</div><div class="line" data-line="13"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;">@<span style="color: #9b9ea4;">doc</span> <span style="color: #b3f6c0;">&quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="14"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">Called for the text/static parts of a template.</span></span></span>
</div><div class="line" data-line="15"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">It must return the updated state.</span></span></span>
</div><div class="line" data-line="16"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">&quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="17"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">callback <span style="color: #8cf8f7;">handle_text</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">line: </span><span style="color: #e0e2ea;">pos_integer</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">column: </span><span style="color: #e0e2ea;">pos_integer</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">text</span> <span style="color: #e0e2ea;">::</span> <span style="color: #e0e2ea;">String</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">t</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">::</span></span></span></span></span>
</div><div class="line" data-line="18"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;"><span style="color: #8cf8f7;"><span style="color: #e0e2ea;">            <span style="color: #e0e2ea;">state</span></span></span></span></span>
</div><div class="line" data-line="19">
</div><div class="line" data-line="20"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;">@<span style="color: #9b9ea4;">doc</span> <span style="color: #b3f6c0;">&quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="21"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">Called for the dynamic/code parts of a template.</span></span></span>
</div><div class="line" data-line="22"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">The marker is what follows exactly after `&lt;%`. For example,</span></span></span>
</div><div class="line" data-line="23"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">`&lt;% foo %&gt;` has an empty marker, but `&lt;%= foo %&gt;` has `&quot;=&quot;`</span></span></span>
</div><div class="line" data-line="24"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">as marker. The allowed markers so far are:</span></span></span>
</div><div class="line" data-line="25"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">  * </span>`&quot;&quot;`</span></span></span>
</div><div class="line" data-line="26"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">  <span style="color: #8cf8f7;">* </span>`&quot;=&quot;`</span></span></span>
</div><div class="line" data-line="27"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">  <span style="color: #8cf8f7;">* </span>`&quot;/&quot;`</span></span></span>
</div><div class="line" data-line="28"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">  <span style="color: #8cf8f7;">* </span>`&quot;|&quot;`</span></span></span>
</div><div class="line" data-line="29"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">Markers `&quot;/&quot;` and `&quot;|&quot;` are only for use in custom EEx engines</span></span></span>
</div><div class="line" data-line="30"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">and are not implemented by default. Using them without an</span></span></span>
</div><div class="line" data-line="31"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">appropriate implementation raises `EEx.SyntaxError`.</span></span></span>
</div><div class="line" data-line="32"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">It must return the updated state.</span></span></span>
</div><div class="line" data-line="33"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">&quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="34"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">callback <span style="color: #8cf8f7;">handle_expr</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">marker</span> <span style="color: #e0e2ea;">::</span> <span style="color: #e0e2ea;">String</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">t</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">expr</span> <span style="color: #e0e2ea;">::</span> <span style="color: #e0e2ea;">Macro</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">t</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">::</span> <span style="color: #e0e2ea;">state</span></span></span></span></span>
</div><div class="line" data-line="35">
</div><div class="line" data-line="36"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;">@<span style="color: #9b9ea4;">doc</span> <span style="color: #b3f6c0;">&quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="37"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">Invoked at the beginning of every nesting.</span></span></span>
</div><div class="line" data-line="38"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">It must return a new state that is used only inside the nesting.</span></span></span>
</div><div class="line" data-line="39"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">Once the nesting terminates, the current `state` is resumed.</span></span></span>
</div><div class="line" data-line="40"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">&quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="41"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">callback <span style="color: #8cf8f7;">handle_begin</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">::</span> <span style="color: #e0e2ea;">state</span></span></span></span></span>
</div><div class="line" data-line="42">
</div><div class="line" data-line="43"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;">@<span style="color: #9b9ea4;">doc</span> <span style="color: #b3f6c0;">&quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="44"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">Invokes at the end of a nesting.</span></span></span>
</div><div class="line" data-line="45"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">It must return Elixir&#39;s quoted expressions for the nesting.</span></span></span>
</div><div class="line" data-line="46"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">&quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="47"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">callback <span style="color: #8cf8f7;">handle_end</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">::</span> <span style="color: #e0e2ea;">Macro</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">t</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span></span></span></span></span>
</div></code></pre>
<p>The compiler will start by calling the <code>c:init</code> callback to initialize the engine's state. It will pass this state to the various other callbacks as required. The state is an implementation detail to the Engine but will usually collect the static and dynamic pieces of the template as it traverses the tokens.</p>
<p>Whenever the compiler encounters a "text" token, it will pass the state, the metadata, and the text to he <code>c:handle_text/3</code> callback.</p>
<p>Whenever the compiler encounters an <code>expression</code> token, it will pass the state, the "marker", and the expression AST to the <code>c:handle_expr/3</code> callback.</p>
<p>If the encountered expression is multi-line, it will call the <code>c:handle_begin/3</code> callback. This creates a new "nesting" and will return a brand new state to be used to collect the output for the "nested" tokens.</p>
<p>When the compiler encounters an <code>:eof</code> token, it knows the template has finished and passes the state to <code>c:handle_body</code>, which returns the Elixir AST for the entire template.</p>
<p>I built an app that allows you to step through the compilation of a template as it traverses the tokens to help visualize what is happening and the output that is produced.</p>
<p><img src="https://user-images.githubusercontent.com/5523984/162604950-a682e940-2af2-4968-886b-7413f256020d.gif" alt="EEx Compiler Visualizer" /></p>
<p>You should check it out <a href="https://github.com/mhanberg/eex_compiler_visualizer">here</a>.</p>
<p>Here is what the compiled output for the above example looks like.</p>
<p>The AST generated by the <code>EEx.SmartEngine</code> forms all dynamic parts of your template into variables, then at the end, creates a <code>bitstring</code> with the result.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">arg0</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">String.Chars</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">to_string</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">EEx.Engine</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">fetch_assign!</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">var!</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">assigns</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:user</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea;">arg1</span> <span style="color: #e0e2ea;">=</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea;">String.Chars</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">to_string</span><span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">user</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">EEx.Engine</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">fetch_assign!</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">var!</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">assigns</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:users</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="6">      <span style="color: #e0e2ea;">arg1</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">String.Chars</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">to_string</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="7">      <span style="color: #e0e2ea;">&lt;&lt;</span><span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">\n</span>    &lt;li&gt;&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">arg1</span><span style="color: #e0e2ea;">::</span><span style="color: #e0e2ea;">binary</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;&lt;/li&gt;<span style="color: #8cf8f7;">\n</span>  &quot;</span><span style="color: #e0e2ea;">&gt;&gt;</span>
</div><div class="line" data-line="8">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="9">  <span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #e0e2ea;">&lt;&lt;</span><span style="color: #b3f6c0;">&quot;&lt;!-- single line expression --&gt;<span style="color: #8cf8f7;">\n</span>&lt;span&gt;&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">arg0</span><span style="color: #e0e2ea;">::</span><span style="color: #e0e2ea;">binary</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="12">  <span style="color: #b3f6c0;">&quot;&lt;/span&gt;<span style="color: #8cf8f7;">\n</span><span style="color: #8cf8f7;">\n</span>&lt;!-- block expression --&gt;<span style="color: #8cf8f7;">\n</span>&lt;ul&gt;<span style="color: #8cf8f7;">\n</span>  &quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">arg1</span><span style="color: #e0e2ea;">::</span><span style="color: #e0e2ea;">binary</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">\n</span>&lt;/ul&gt;&quot;</span><span style="color: #e0e2ea;">&gt;&gt;</span>
</div></code></pre>
<p>Let's take a look at how the <code>Phoenix.HTML.Engine</code> compiles the same template.</p>
<p>We can observe some similarities: it has the same <code>@</code> assigns functionality and it groups dynamic pieces into variables to coordinate later into the final result.</p>
<p>It also packs in some extra features!</p>
<ul>
<li>It will escape dynamically rendered plaintext, so you won't accidentally get some JS injection.</li>
<li>It can pass data through the <code>Phoenix.HTML.Safe</code> protocol, allowing you to make custom representation of your Elixir data structures.</li>
<li>It builds the result into <code>iodata</code> instead of a <code>bitstring</code>.</li>
</ul>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">arg0</span> <span style="color: #e0e2ea;">=</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #e0e2ea;">Phoenix.HTML.Engine</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">fetch_assign!</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">var!</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">assigns</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:user</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="3">    <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:safe</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">data</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">data</span>
</div><div class="line" data-line="4">    <span style="color: #e0e2ea;">bin</span> <span style="color: #e0e2ea; font-weight: bold;">when</span> <span style="color: #8cf8f7;">is_binary</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">bin</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">Phoenix.HTML.Engine</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">html_escape</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">bin</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea;">other</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">Phoenix.HTML.Safe</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">to_iodata</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">other</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8"><span style="color: #e0e2ea;">arg1</span> <span style="color: #e0e2ea;">=</span>
</div><div class="line" data-line="9">  <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">user</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">Phoenix.HTML.Engine</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">fetch_assign!</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">var!</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">assigns</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:users</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="10">          <span style="color: #e0e2ea;">arg1</span> <span style="color: #e0e2ea;">=</span>
</div><div class="line" data-line="11">            <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="12">              <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:safe</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">data</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">data</span>
</div><div class="line" data-line="13">              <span style="color: #e0e2ea;">bin</span> <span style="color: #e0e2ea; font-weight: bold;">when</span> <span style="color: #8cf8f7;">is_binary</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">bin</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">Phoenix.HTML.Engine</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">html_escape</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">bin</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="14">              <span style="color: #e0e2ea;">other</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">Phoenix.HTML.Safe</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">to_iodata</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">other</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="15">            <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="16">
</div><div class="line" data-line="17">          <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:safe</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">\n</span>    &lt;li&gt;&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">arg1</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;&lt;/li&gt;<span style="color: #8cf8f7;">\n</span>  &quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="18">        <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="19">    <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:safe</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">data</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">data</span>
</div><div class="line" data-line="20">    <span style="color: #e0e2ea;">bin</span> <span style="color: #e0e2ea; font-weight: bold;">when</span> <span style="color: #8cf8f7;">is_binary</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">bin</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">Phoenix.HTML.Engine</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">html_escape</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">bin</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="21">    <span style="color: #e0e2ea;">other</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">Phoenix.HTML.Safe</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">to_iodata</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">other</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="22">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="23">
</div><div class="line" data-line="24"><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:safe</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="25"> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="26">   <span style="color: #b3f6c0;">&quot;&lt;!-- single line expression --&gt;<span style="color: #8cf8f7;">\n</span>&lt;span&gt;&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="27">   <span style="color: #e0e2ea;">arg0</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="28">   <span style="color: #b3f6c0;">&quot;&lt;/span&gt;<span style="color: #8cf8f7;">\n</span><span style="color: #8cf8f7;">\n</span>&lt;!-- block expression --&gt;<span style="color: #8cf8f7;">\n</span>&lt;ul&gt;<span style="color: #8cf8f7;">\n</span>  &quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="29">   <span style="color: #e0e2ea;">arg1</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="30">   <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">\n</span>&lt;/ul&gt;&quot;</span>
</div><div class="line" data-line="31"> <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<p>Let's also peek at the output of <code>Phoenix.LiveView.Engine</code>, as it will generate something fairly different than the previous two engines.</p>
<p>The LiveView engine will emit these <code>%Rendered&lbrace;&rbrace;</code> and <code>%Comprehension&lbrace;&rbrace;</code> structs that allow it to efficiently do its diff tracking.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">require</span> <span style="color: #e0e2ea;">Phoenix.LiveView.Engine</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea;">dynamic</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">track_changes?</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea;">changed</span> <span style="color: #e0e2ea;">=</span>
</div><div class="line" data-line="6">      <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #e0e2ea;">assigns</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="7">        <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">__changed__: </span><span style="color: #e0e2ea;">changed</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea; font-weight: bold;">when</span> <span style="color: #e0e2ea;">track_changes?</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">changed</span>
</div><div class="line" data-line="8">        <span style="color: #9b9ea4;">_</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #8cf8f7;">nil</span>
</div><div class="line" data-line="9">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11">    <span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="12">      <span style="color: #e0e2ea;">v0</span> <span style="color: #e0e2ea;">=</span>
</div><div class="line" data-line="13">        <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #e0e2ea;">Phoenix.LiveView.Engine</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">nested_changed_assign?</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">assigns</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">changed</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:user</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">struct: </span><span style="color: #8cf8f7;">:name</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="14">          <span style="color: #e0e2ea;">true</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">Phoenix.LiveView.Engine</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">live_to_iodata</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">assigns</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="15">          <span style="color: #e0e2ea;">false</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #8cf8f7;">nil</span>
</div><div class="line" data-line="16">        <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="17">
</div><div class="line" data-line="18">      <span style="color: #e0e2ea;">v2</span> <span style="color: #e0e2ea;">=</span>
</div><div class="line" data-line="19">        <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #e0e2ea;">Phoenix.LiveView.Engine</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">changed_assign?</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">changed</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:users</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="20">          <span style="color: #e0e2ea;">true</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="21">            <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">Phoenix.LiveView.Comprehension</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="22">              <span style="color: #8cf8f7;">static: </span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">\n</span>    &lt;li&gt;&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;&lt;/li&gt;<span style="color: #8cf8f7;">\n</span>  &quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="23">              <span style="color: #8cf8f7;">dynamics:</span>
</div><div class="line" data-line="24"><span style="color: #8cf8f7;"></span>                <span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">user</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">assigns</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">users</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="25">                  <span style="color: #e0e2ea;">v1</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">Phoenix.LiveView.Engine</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">live_to_iodata</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="26">                  <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">v1</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="27">                <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="28">              <span style="color: #8cf8f7;">fingerprint: </span><span style="color: #e0e2ea;">245_710_792_584_248_035_419_893_716_549_685_965_177</span>
</div><div class="line" data-line="29">            <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="30">
</div><div class="line" data-line="31">          <span style="color: #e0e2ea;">false</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="32">            <span style="color: #8cf8f7;">nil</span>
</div><div class="line" data-line="33">        <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="34">    <span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="35">
</div><div class="line" data-line="36">    <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">v0</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">v2</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="37">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="38">
</div><div class="line" data-line="39">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">Phoenix.LiveView.Rendered</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="40">    <span style="color: #8cf8f7;">static: </span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="41">      <span style="color: #b3f6c0;">&quot;&lt;!-- single line expression --&gt;<span style="color: #8cf8f7;">\n</span>&lt;span&gt;&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="42">      <span style="color: #b3f6c0;">&quot;&lt;/span&gt;<span style="color: #8cf8f7;">\n</span><span style="color: #8cf8f7;">\n</span>&lt;!-- block expression --&gt;<span style="color: #8cf8f7;">\n</span>&lt;ul&gt;<span style="color: #8cf8f7;">\n</span>  &quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="43">      <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">\n</span>&lt;/ul&gt;&quot;</span>
</div><div class="line" data-line="44">    <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="45">    <span style="color: #8cf8f7;">dynamic: </span><span style="color: #e0e2ea;">dynamic</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="46">    <span style="color: #8cf8f7;">fingerprint: </span><span style="color: #e0e2ea;">104_434_365_286_405_865_087_616_174_532_871_347_220</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="47">    <span style="color: #8cf8f7;">root: </span><span style="color: #8cf8f7;">nil</span>
</div><div class="line" data-line="48">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="49"><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<h2><a href="#how-can-this-help-temple" aria-hidden="true" class="anchor" id="how-can-this-help-temple"></a>How can this help Temple?</h2>
<p>So far we've detailed how <em>Elixir</em> will compile an EEx template. The <code>EEx.Tokenizer</code> and <code>EEx.Compiler</code> are not public modules and are essentially an implementation detail.</p>
<p>Surface and HEEx are not EEx compatible, so attempting to compile them with <code>EEx.compile_string/2</code> would raise a syntax error. So how do they work?</p>
<p>They both implement their own tokenizer and compiler but delegate to the engine! Temple can do the exact same thing.</p>
<p>The <em>Engine</em> is the public API that Temple can leverage to efficiently output Elixir AST for the various compile targets: Non-Phoenix web apps (for use in Plug, <a href="https://github.com/oestrich/aino">Aino</a>, any non-Phoenix web framework), traditional Phoenix Apps, and Phoenix LiveView apps. This is the technique that José was explaining that HEEx and Surface use.</p>
<p>Here is a table comparing which parts of the "template to Elixir AST" lifestyle between the builtin EEx compiler, the Temple compiler (in a theoretical future when Temple leverages this technique), Surface, and HEEx. EEx and HEEx don't have their own AST, so the parser step is not applicable.</p>
<table>
<thead>
<tr>
<th>Step</th>
<th>EEx</th>
<th>Temple</th>
<th>Surface</th>
<th>HEEx</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tokenizer</td>
<td>EEx.Tokenizer</td>
<td>Kernel.SpecialForms.quote/2</td>
<td>Surface.Compiler.Tokenizer</td>
<td>Phoenix.LiveView.HTMLTokenizer</td>
</tr>
<tr>
<td>Parser</td>
<td>-</td>
<td>Temple.Parser</td>
<td>Surface.Compiler.Parser</td>
<td>-</td>
</tr>
<tr>
<td>"Compiler"</td>
<td>EEx.Compiler</td>
<td>Temple.Renderer</td>
<td>Surface.Compiler.EExEngine</td>
<td>Phoenix.LiveView.HTMLEngine</td>
</tr>
<tr>
<td>Engine</td>
<td>Chosen Engine</td>
<td>Chosen Engine</td>
<td>Phoenix.LiveView.Engine</td>
<td>Chosen Engine</td>
</tr>
</tbody>
</table>
<p>Prior to this revelation, I had planned on the monumental task of basically re-implementing the <code>EEx.SmartEngine</code>, <code>Phoenix.HTML.Engine</code>, and <code>Phoenix.LiveView.Engine</code> modules in Temple (probably with lots of bugs). Now that I understand how to utilize the existing engines properly, it should be a relatively small lift for Temple to be able to use all three of them!</p>
<p>🎉 Kudos to José and Marlus for the advice, I really appreciate it!</p> ]]></description>
    </item>
    <item>
       <title>How I Handle Static Assets in my Phoenix apps</title>
       <link>https://www.mitchellhanberg.com/how-i-handle-static-assets-in-my-phoenix-apps/</link>
       <pubDate>Mon, 07 Jun 2021 01:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/how-i-handle-static-assets-in-my-phoenix-apps/</guid>
       <description><![CDATA[ <p>I no longer use <a href="https://webpack.js.org/">Webpack</a>.</p>
<p>Now I use <a href="https://esbuild.github.io/">esbuild</a>, <a href="https://github.com/postcss/postcss-cli">postcss-cli</a>, and <a href="https://github.com/mysticatea/cpx">cpx</a>.</p>
<ul>
<li>esbuild bundles and transpiles my JavaScript files extremely fast.</li>
<li>postcss-cli processes my CSS and can run the <a href="https://tailwindcss.com/docs/just-in-time-mode">TailwindCSS JIT</a> mode without any problems.</li>
<li>cpx copies other static files from the <code>assets</code> directory into the <code>priv</code> directory.</li>
</ul>
<p>Running each of these tools in development is just as easy as running Webpack.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;"># config/dev.exs</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea;">esbuild</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">Path</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">expand</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;../assets/node_modules/.bin/esbuild&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">__DIR__</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="4"><span style="color: #8cf8f7;">config</span> <span style="color: #8cf8f7;">:my_app</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">MyAppWeb.Endpoint</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5">  <span style="color: #8cf8f7;">http: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">port: </span><span style="color: #e0e2ea;">4000</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="6">  <span style="color: #8cf8f7;">debug_errors: </span><span style="color: #e0e2ea;">true</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">  <span style="color: #8cf8f7;">code_reloader: </span><span style="color: #e0e2ea;">true</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="8">  <span style="color: #8cf8f7;">check_origin: </span><span style="color: #e0e2ea;">false</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="9">  <span style="color: #8cf8f7;">watchers: </span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="10">    <span style="color: #8cf8f7;">&quot;<span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">esbuild</span><span style="color: #8cf8f7;">&rbrace;</span>&quot;: </span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="11">      <span style="color: #b3f6c0;">&quot;./js/app.js&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="12">      <span style="color: #b3f6c0;">&quot;--target=es2015&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="13">      <span style="color: #b3f6c0;">&quot;--bundle&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="14">      <span style="color: #b3f6c0;">&quot;--outdir=../priv/static/js&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="15">      <span style="color: #b3f6c0;">&quot;--sourcemap&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="16">      <span style="color: #b3f6c0;">&quot;--watch&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="17">      <span style="color: #8cf8f7;">cd: </span><span style="color: #e0e2ea;">Path</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">expand</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;../assets&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">__DIR__</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="18">    <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="19">    <span style="color: #8cf8f7;">node: </span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="20">      <span style="color: #b3f6c0;">&quot;./node_modules/.bin/postcss&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="21">      <span style="color: #b3f6c0;">&quot;./css/app.css&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="22">      <span style="color: #b3f6c0;">&quot;--dir&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="23">      <span style="color: #b3f6c0;">&quot;../priv/static/css&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="24">      <span style="color: #b3f6c0;">&quot;-w&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="25">      <span style="color: #8cf8f7;">cd: </span><span style="color: #e0e2ea;">Path</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">expand</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;../assets&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">__DIR__</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="26">      <span style="color: #8cf8f7;">env: </span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #b3f6c0;">&quot;NODE_ENV&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;development&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #b3f6c0;">&quot;TAILWIND_MODE&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;watch&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="27">    <span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="28">    <span style="color: #8cf8f7;">node: </span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="29">      <span style="color: #b3f6c0;">&quot;./node_modules/.bin/cpx&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="30">      <span style="color: #b3f6c0;">&quot;&#39;static/**/*&#39;&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="31">      <span style="color: #b3f6c0;">&quot;../priv/static&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="32">      <span style="color: #b3f6c0;">&quot;--watch&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="33">      <span style="color: #8cf8f7;">cd: </span><span style="color: #e0e2ea;">Path</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">expand</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;../assets&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">__DIR__</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="34">    <span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="35">  <span style="color: #e0e2ea;">]</span>
</div></code></pre>
<h2><a href="#esbuild" aria-hidden="true" class="anchor" id="esbuild"></a>esbuild</h2>
<p><img src="https://res.cloudinary.com/mhanberg/image/upload/v1622864248/Screen_Shot_2021-06-04_at_11.36.48_PM.png" alt="esbuild home page" /></p>
<p>esbuild is part of the newest generation of JavaScript tooling. It is built in <a href="https://golang.org/">Go</a> and is <em>extremely</em> fast. All I really need is my JavaScript to be bundled and transpiled to some degree, and esbuild does just that.</p>
<p>Since esbuild is not available in our path, we'll have to declare our watcher with the absolute path to the binary. To make this legible, I have pulled out the absolute path to a variable so it reads more like what we are used to.</p>
<h2><a href="#postcss" aria-hidden="true" class="anchor" id="postcss"></a>PostCSS</h2>
<p>Since I'm are no longer using Webpack, I just use the <code>postcss-cli</code>.</p>
<p>The third highlighted line demonstrates that this configuration is really just a declarative way to run commands with <a href="https://hexdocs.pm/elixir/System.html#cmd/2"><code>System.cmd/3</code></a>. This means I can use all the same options! I am using the new <a href="https://tailwindcss.com/docs/just-in-time-mode">TailwindCSS JIT</a>, which requires a couple of environment variables to be set, so I pass the <code>:env</code> option to include variables in the environment of PostCSS.</p>
<h2><a href="#cpx" aria-hidden="true" class="anchor" id="cpx"></a>cpx</h2>
<p>Once I ditched Webpack, I realized that in addition to bundling my JS and CSS, it was responsible for copying my other static assets to the <code>priv</code> directory. Luckily there is this handy npm package called <code>cpx</code> that will watch a glob of directories and copy them somewhere else when it notices changes.</p>
<h2><a href="#wrapping-up" aria-hidden="true" class="anchor" id="wrapping-up"></a>Wrapping Up</h2>
<p>The speed increase from switching from Webpack to esbuild is well worth the effort to make the switch.</p>
<p>Do you use something other than Webpack in your Phoenix applications? Let me know on <a href="https://twitter.com/mitchhanberg">Twitter</a>!</p> ]]></description>
    </item>
    <item>
       <title>How to Set Up Neovim for Elixir Development</title>
       <link>https://www.mitchellhanberg.com/how-to-set-up-neovim-for-elixir-development/</link>
       <pubDate>Wed, 02 Jun 2021 09:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/how-to-set-up-neovim-for-elixir-development/</guid>
       <description><![CDATA[ <div class="markdown-alert markdown-alert-important">
<p class="markdown-alert-title">Update</p>
<p>4/12/22: I've started working on a dedicated Neovim plugin for Elixir called <a href="https://github.com/elixir-tools/elixir-tools.nvim">elixir-tools.nvim</a>. Please follow me on <a href="https://twitter.com/mitchhanberg">Twitter</a> for more frequent updates!</p>
</div>
<p>This article is the spiritual successor to <a href="/post/2018/10/18/how-to-use-elixir-ls-with-vim/">How to use Elixir LS with Vim</a>.</p>
<p>Since then, I've switched from Vim to the nightly release of <a href="https://github.com/neovim/neovim">Neovim</a> as well as how I integrate linters, formatters, and LSPs.</p>
<p>This article will cover:</p>
<ul>
<li>Installing Neovim</li>
<li>Getting started with the builtin LSP client</li>
<li>Setting up Elixir LS</li>
<li>Integrating <a href="https://github.com/rrrene/credo">Credo</a></li>
</ul>
<p><em>If you run into any problems with this guide, feel free to shoot me an <a href="mailto:contact@mitchellhanberg.com">email</a>.</em></p>
<p>Let's get started!</p>
<h2><a href="#installing-neovim" aria-hidden="true" class="anchor" id="installing-neovim"></a>Installing Neovim</h2>
<p>You can install Neovim with your package manager of choice, or <a href="https://asdf-vm.com">asdf</a>.</p>
<h3><a href="#homebrew" aria-hidden="true" class="anchor" id="homebrew"></a>Homebrew</h3>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ brew install neovim
</div></code></pre>
<h3><a href="#asdf" aria-hidden="true" class="anchor" id="asdf"></a>asdf</h3>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ asdf plugin add neovim
</div><div class="line" data-line="2">
</div><div class="line" data-line="3">$ asdf install neovim stable
</div></code></pre>
<h3><a href="#nightly" aria-hidden="true" class="anchor" id="nightly"></a>Nightly</h3>
<blockquote>
<p>With the release of 0.5, you no longer need to use Neovim nightly for this setup. I will leave these instructions in case you still want to live on the edge.</p>
</blockquote>
<p>As of this writing, the builtin LSP client is only available on the nightly build of Neovim. Once 0.5 is released, you should be able to switch to a stable build, but for now, let's get nightly installed.</p>
<p>My preferred method for managing my installation of Neovim is to use <a href="https://asdf-vm.com">asdf</a>. You can install it with <a href="https://brew.sh">Homebrew</a> as well, but I find asdf to be better.</p>
<p>For this article, I am going to assume you already have asdf installed, as it is the most prevalent way to manage Elixir and Erlang installations.</p>
<p>But we still need to install the Neovim asdf plugin.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ asdf plugin add neovim
</div></code></pre>
<p>Listing the available versions demonstrates that we can install any previously released version of Neovim, as well as the nightly build. These versions are pre-built and downloaded as a GitHub release artifact. This makes installing them very fast.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ asdf list all neovim
</div><div class="line" data-line="2">0.1.0
</div><div class="line" data-line="3">0.1.1
</div><div class="line" data-line="4">0.1.2
</div><div class="line" data-line="5">0.1.3
</div><div class="line" data-line="6">0.1.4
</div><div class="line" data-line="7">0.1.5
</div><div class="line" data-line="8">0.1.6
</div><div class="line" data-line="9">0.1.7
</div><div class="line" data-line="10">0.2.0
</div><div class="line" data-line="11">0.2.1
</div><div class="line" data-line="12">0.2.2
</div><div class="line" data-line="13">0.3.0
</div><div class="line" data-line="14">0.3.1
</div><div class="line" data-line="15">0.3.2
</div><div class="line" data-line="16">0.3.3
</div><div class="line" data-line="17">0.3.4
</div><div class="line" data-line="18">0.3.5
</div><div class="line" data-line="19">0.3.6
</div><div class="line" data-line="20">0.3.7
</div><div class="line" data-line="21">0.3.8
</div><div class="line" data-line="22">0.4.0
</div><div class="line" data-line="23">0.4.1
</div><div class="line" data-line="24">0.4.2
</div><div class="line" data-line="25">0.4.3
</div><div class="line" data-line="26">0.4.4
</div><div class="line" data-line="27">nightly
</div><div class="line" data-line="28">stable
</div></code></pre>
<p>If for some reason the nightly build cron job is on the fritz (as it sometimes is), you can also build form source with:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ asdf install neovim ref:master
</div></code></pre>
<p>What we are going to stick with is:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ asdf install neovim nightly
</div><div class="line" data-line="2">$ asdf global neovim nightly
</div></code></pre>
<p>Now, if you want to <em>update</em> your nightly installation, all you have to do is uninstall and reinstall. I use the following as a convenient shell alias.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ alias update-nvim-nightly=&#39;asdf uninstall neovim nightly &amp;&amp; asdf install neovim nightly&#39;
</div></code></pre>
<p>So, just to tie this all together, these are the steps you will go through to get Neovim nightly installed.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ asdf plugin add neovim
</div><div class="line" data-line="2">$ asdf install neovim nightly
</div><div class="line" data-line="3">$ asdf global neovim nightly
</div><div class="line" data-line="4">$ echo &quot;alias update-nvim-nightly=&#39;asdf uninstall neovim nightly &amp;&amp; asdf install neovim nightly&#39;&quot; &gt;&gt; .zshrc # bash/fish/etc
</div></code></pre>
<h2><a href="#getting-started-with-the-builtin-lsp-client" aria-hidden="true" class="anchor" id="getting-started-with-the-builtin-lsp-client"></a>Getting started with the builtin LSP client</h2>
<p>To help users get started with the LSP client, the Neovim team provides a plugin called <a href="https://github.com/neovim/nvim-lspconfig">nvim-lspconfig</a> that contains configurations for many common language servers.</p>
<p>There are also a few other plugins revolving around autocomplete that you'll need to install to get the full LSP experience.</p>
<p>With your preferred plugin manager, install the following plugins. I'm using <a href="https://github.com/wbthomason/packer.nvim">packer.nvim</a>. If you don't use a package manager, I suggest learning more about them by checking out packer.nvim as well as <a href="https://github.com/junegunn/vim-plug">vim-plug</a>.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-lua" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cmd</span> <span style="color: #b3f6c0;">[[<span style="color: #e0e2ea; font-weight: bold;">packadd</span> packer.nvim]]</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">local</span> <span style="color: #e0e2ea;">startup</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">require</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;packer&quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">startup</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #8cf8f7;">startup</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">function</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">use</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="6">  <span style="color: #9b9ea4;">-- language server configurations</span>
</div><div class="line" data-line="7">  <span style="color: #8cf8f7;">use</span> <span style="color: #b3f6c0;">&quot;neovim/nvim-lspconfig&quot;</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">  <span style="color: #9b9ea4;">-- autocomplete and snippets</span>
</div><div class="line" data-line="10">  <span style="color: #8cf8f7;">use</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;hrsh7th/nvim-cmp&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="11">  <span style="color: #8cf8f7;">use</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;hrsh7th/cmp-nvim-lsp&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="12">  <span style="color: #8cf8f7;">use</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;hrsh7th/cmp-vsnip&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="13">  <span style="color: #8cf8f7;">use</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;hrsh7th/vim-vsnip&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="14">
</div><div class="line" data-line="15">  <span style="color: #8cf8f7;">use</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;onsails/lspkind-nvim&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="16"><span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<p>Now that we have the required plugins installed, let's set them up so they get booted when we start Neovim. I am using the <code>init.lua</code> config file, but you can also add this to your <code>init.vim</code> if you use a lua heredoc.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-lua" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">local</span> <span style="color: #e0e2ea;">lspconfig</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">require</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;lspconfig&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #9b9ea4;">-- Neovim doesn&#39;t support snippets out of the box, so we need to mutate the</span>
</div><div class="line" data-line="4"><span style="color: #9b9ea4;">-- capabilities we send to the language server to let them know we want snippets.</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea; font-weight: bold;">local</span> <span style="color: #e0e2ea;">capabilities</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">lsp</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">protocol</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">make_client_capabilities</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="6"><span style="color: #e0e2ea;">capabilities</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">textDocument</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">completion</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">completionItem</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">snippetSupport</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">true</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8"><span style="color: #9b9ea4;">-- Setup our autocompletion. These configuration options are the default ones</span>
</div><div class="line" data-line="9"><span style="color: #9b9ea4;">-- copied out of the documentation.</span>
</div><div class="line" data-line="10"><span style="color: #e0e2ea; font-weight: bold;">local</span> <span style="color: #e0e2ea;">cmp</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">require</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;cmp&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="11">
</div><div class="line" data-line="12"><span style="color: #e0e2ea;">cmp</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">setup</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="13">  <span style="color: #a6dbff;">snippet</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="14">    <span style="color: #8cf8f7;">expand</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea; font-weight: bold;">function</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">args</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="15">      <span style="color: #9b9ea4;">-- For `vsnip` user.</span>
</div><div class="line" data-line="16">      <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">fn</span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;vsnip#anonymous&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">args</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">body</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="17">    <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="18">  <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="19">  <span style="color: #a6dbff;">mapping</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="20">    <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;&lt;C-b&gt;&quot;</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">cmp</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">mapping</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">scroll_docs</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">-</span><span style="color: #e0e2ea;">4</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="21">    <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;&lt;C-f&gt;&quot;</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">cmp</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">mapping</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">scroll_docs</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">4</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="22">    <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;&lt;C-Space&gt;&quot;</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">cmp</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">mapping</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">complete</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="23">    <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;&lt;C-e&gt;&quot;</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">cmp</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">mapping</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">close</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="24">    <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;&lt;C-y&gt;&quot;</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">cmp</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">mapping</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">confirm</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">&lbrace;</span> <span style="color: #a6dbff;">select</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">true</span> <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="25">  <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="26">  <span style="color: #a6dbff;">sources</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="27">    <span style="color: #8cf8f7;">&lbrace;</span> <span style="color: #a6dbff;">name</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;nvim_lsp&quot;</span> <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="28">    <span style="color: #8cf8f7;">&lbrace;</span> <span style="color: #a6dbff;">name</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;vsnip&quot;</span> <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="29">  <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="30">  <span style="color: #a6dbff;">formatting</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="31">    <span style="color: #a6dbff;">format</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">require</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;lspkind&quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cmp_format</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="32">      <span style="color: #a6dbff;">with_text</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">true</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="33">      <span style="color: #a6dbff;">menu</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="34">        <span style="color: #a6dbff;">nvim_lsp</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;[LSP]&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="35">      <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="36">    <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="37">  <span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="38"><span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<p>That should be it for the basic LSP client configuration.</p>
<h2><a href="#setting-up-elixir-ls" aria-hidden="true" class="anchor" id="setting-up-elixir-ls"></a>Setting up Elixir LS</h2>
<p><a href="https://github.com/elixir-lsp/elixir-ls">Elixir LS</a> is a tool that needs to be compiled from source, but it's pretty simple. Pick a directory to install and run the following commands.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ git clone git@github.com:elixir-lsp/elixir-ls.git
</div><div class="line" data-line="2">$ cd elixir-ls &amp;&amp; mkdir rel
</div><div class="line" data-line="3">
</div><div class="line" data-line="4"># checkout the latest release if you&#39;d like
</div><div class="line" data-line="5">$ git checkout tags/v0.7.0
</div><div class="line" data-line="6">
</div><div class="line" data-line="7">$ mix deps.get &amp;&amp; mix compile
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">$ mix elixir_ls.release -o release
</div></code></pre>
<p>Now that we have Elixir LS installed and compiled, let's get it set up in Neovim.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-lua" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;">-- A callback that will get called when a buffer connects to the language server.</span>
</div><div class="line" data-line="2"><span style="color: #9b9ea4;">-- Here we create any key maps that we want to have on that buffer.</span>
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">local</span> <span style="color: #8cf8f7;">on_attach</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea; font-weight: bold;">function</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">_</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">bufnr</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">local</span> <span style="color: #e0e2ea; font-weight: bold;">function</span> <span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">...</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">api</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">nvim_buf_set_keymap</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">bufnr</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">...</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="7">  <span style="color: #e0e2ea; font-weight: bold;">local</span> <span style="color: #e0e2ea;">map_opts</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">&lbrace;</span><span style="color: #a6dbff;">noremap</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">true</span><span style="color: #e0e2ea;">,</span> <span style="color: #a6dbff;">silent</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">true</span><span style="color: #8cf8f7;">&rbrace;</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">  <span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;n&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;df&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;&lt;cmd&gt;lua vim.lsp.buf.formatting()&lt;cr&gt;&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">map_opts</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="10">  <span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;n&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;gd&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;&lt;cmd&gt;lua vim.lsp.diagnostic.show_line_diagnostics()&lt;cr&gt;&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">map_opts</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="11">  <span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;n&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;dt&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;&lt;cmd&gt;lua vim.lsp.buf.definition()&lt;cr&gt;&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">map_opts</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="12">  <span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;n&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;K&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;&lt;cmd&gt;lua vim.lsp.buf.hover()&lt;cr&gt;&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">map_opts</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="13">  <span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;n&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;gD&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;&lt;cmd&gt;lua vim.lsp.buf.implementation()&lt;cr&gt;&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">map_opts</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="14">  <span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;n&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;&lt;c-k&gt;&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;&lt;cmd&gt;lua vim.lsp.buf.signature_help()&lt;cr&gt;&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">map_opts</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="15">  <span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;n&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;1gD&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;&lt;cmd&gt;lua vim.lsp.buf.type_definition()&lt;cr&gt;&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">map_opts</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="16">
</div><div class="line" data-line="17">  <span style="color: #9b9ea4;">-- These have a different style than above because I was fiddling</span>
</div><div class="line" data-line="18">  <span style="color: #9b9ea4;">-- around and never converted them. Instead of converting them</span>
</div><div class="line" data-line="19">  <span style="color: #9b9ea4;">-- now, I&#39;m leaving them as they are for this article because this is</span>
</div><div class="line" data-line="20">  <span style="color: #9b9ea4;">-- what I actually use, and hey, it works ¯\_(ツ)_/¯.</span>
</div><div class="line" data-line="21">  <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cmd</span> <span style="color: #b3f6c0;">[[<span style="color: #e0e2ea; font-weight: bold;">imap</span> <span style="color: #8cf8f7;">&lt;expr&gt;</span> <span style="color: #8cf8f7;"><span style="color: #e0e2ea;">&lt;</span>C-l<span style="color: #e0e2ea;">&gt;</span></span> <span style="color: #8cf8f7;">vsnip#available</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">?</span> <span style="color: #b3f6c0;">&#39;&lt;Plug&gt;(vsnip-expand-or-jump)&#39;</span> <span style="color: #e0e2ea; font-weight: bold;">:</span> <span style="color: #b3f6c0;">&#39;&lt;C-l&gt;&#39;</span>]]</span>
</div><div class="line" data-line="22">  <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cmd</span> <span style="color: #b3f6c0;">[[<span style="color: #e0e2ea; font-weight: bold;">smap</span> <span style="color: #8cf8f7;">&lt;expr&gt;</span> <span style="color: #8cf8f7;"><span style="color: #e0e2ea;">&lt;</span>C-l<span style="color: #e0e2ea;">&gt;</span></span> <span style="color: #8cf8f7;">vsnip#available</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">?</span> <span style="color: #b3f6c0;">&#39;&lt;Plug&gt;(vsnip-expand-or-jump)&#39;</span> <span style="color: #e0e2ea; font-weight: bold;">:</span> <span style="color: #b3f6c0;">&#39;&lt;C-l&gt;&#39;</span>]]</span>
</div><div class="line" data-line="23">
</div><div class="line" data-line="24">  <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cmd</span> <span style="color: #b3f6c0;">[[<span style="color: #e0e2ea; font-weight: bold;">imap</span> <span style="color: #8cf8f7;">&lt;expr&gt;</span> <span style="color: #8cf8f7;"><span style="color: #e0e2ea;">&lt;</span>Tab<span style="color: #e0e2ea;">&gt;</span></span> <span style="color: #8cf8f7;">vsnip#jumpable</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">?</span> <span style="color: #b3f6c0;">&#39;&lt;Plug&gt;(vsnip-jump-next)&#39;</span> <span style="color: #e0e2ea; font-weight: bold;">:</span> <span style="color: #b3f6c0;">&#39;&lt;Tab&gt;&#39;</span>]]</span>
</div><div class="line" data-line="25">  <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cmd</span> <span style="color: #b3f6c0;">[[<span style="color: #e0e2ea; font-weight: bold;">smap</span> <span style="color: #8cf8f7;">&lt;expr&gt;</span> <span style="color: #8cf8f7;"><span style="color: #e0e2ea;">&lt;</span>Tab<span style="color: #e0e2ea;">&gt;</span></span> <span style="color: #8cf8f7;">vsnip#jumpable</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">?</span> <span style="color: #b3f6c0;">&#39;&lt;Plug&gt;(vsnip-jump-next)&#39;</span> <span style="color: #e0e2ea; font-weight: bold;">:</span> <span style="color: #b3f6c0;">&#39;&lt;Tab&gt;&#39;</span>]]</span>
</div><div class="line" data-line="26">  <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cmd</span> <span style="color: #b3f6c0;">[[<span style="color: #e0e2ea; font-weight: bold;">imap</span> <span style="color: #8cf8f7;">&lt;expr&gt;</span> <span style="color: #8cf8f7;"><span style="color: #e0e2ea;">&lt;</span>S-Tab<span style="color: #e0e2ea;">&gt;</span></span> <span style="color: #8cf8f7;">vsnip#jumpable</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">-1</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">?</span> <span style="color: #b3f6c0;">&#39;&lt;Plug&gt;(vsnip-jump-prev)&#39;</span> <span style="color: #e0e2ea; font-weight: bold;">:</span> <span style="color: #b3f6c0;">&#39;&lt;S-Tab&gt;&#39;</span>]]</span>
</div><div class="line" data-line="27">  <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cmd</span> <span style="color: #b3f6c0;">[[<span style="color: #e0e2ea; font-weight: bold;">smap</span> <span style="color: #8cf8f7;">&lt;expr&gt;</span> <span style="color: #8cf8f7;"><span style="color: #e0e2ea;">&lt;</span>S-Tab<span style="color: #e0e2ea;">&gt;</span></span> <span style="color: #8cf8f7;">vsnip#jumpable</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">-1</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">?</span> <span style="color: #b3f6c0;">&#39;&lt;Plug&gt;(vsnip-jump-prev)&#39;</span> <span style="color: #e0e2ea; font-weight: bold;">:</span> <span style="color: #b3f6c0;">&#39;&lt;S-Tab&gt;&#39;</span>]]</span>
</div><div class="line" data-line="28">
</div><div class="line" data-line="29">  <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cmd</span> <span style="color: #b3f6c0;">[[<span style="color: #e0e2ea; font-weight: bold;">inoremap</span> <span style="color: #8cf8f7;">&lt;silent&gt;</span><span style="color: #8cf8f7;">&lt;expr&gt;</span> <span style="color: #8cf8f7;"><span style="color: #e0e2ea;">&lt;</span>C-Space<span style="color: #e0e2ea;">&gt;</span></span> <span style="color: #8cf8f7;">compe#complete</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>]]</span>
</div><div class="line" data-line="30">  <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cmd</span> <span style="color: #b3f6c0;">[[<span style="color: #e0e2ea; font-weight: bold;">inoremap</span> <span style="color: #8cf8f7;">&lt;silent&gt;</span><span style="color: #8cf8f7;">&lt;expr&gt;</span> <span style="color: #8cf8f7;"><span style="color: #e0e2ea;">&lt;</span>CR<span style="color: #e0e2ea;">&gt;</span></span> <span style="color: #8cf8f7;">compe#confirm</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&#39;&lt;CR&gt;&#39;</span><span style="color: #e0e2ea;">)</span>]]</span>
</div><div class="line" data-line="31">  <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cmd</span> <span style="color: #b3f6c0;">[[<span style="color: #e0e2ea; font-weight: bold;">inoremap</span> <span style="color: #8cf8f7;">&lt;silent&gt;</span><span style="color: #8cf8f7;">&lt;expr&gt;</span> <span style="color: #8cf8f7;"><span style="color: #e0e2ea;">&lt;</span>C-e<span style="color: #e0e2ea;">&gt;</span></span> <span style="color: #8cf8f7;">compe#close</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&#39;&lt;C-e&gt;&#39;</span><span style="color: #e0e2ea;">)</span>]]</span>
</div><div class="line" data-line="32">  <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cmd</span> <span style="color: #b3f6c0;">[[<span style="color: #e0e2ea; font-weight: bold;">inoremap</span> <span style="color: #8cf8f7;">&lt;silent&gt;</span><span style="color: #8cf8f7;">&lt;expr&gt;</span> <span style="color: #8cf8f7;"><span style="color: #e0e2ea;">&lt;</span>C-f<span style="color: #e0e2ea;">&gt;</span></span> <span style="color: #8cf8f7;">compe#scroll</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #b3f6c0;">&#39;delta&#39;</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">+4</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>]]</span>
</div><div class="line" data-line="33">  <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">cmd</span> <span style="color: #b3f6c0;">[[<span style="color: #e0e2ea; font-weight: bold;">inoremap</span> <span style="color: #8cf8f7;">&lt;silent&gt;</span><span style="color: #8cf8f7;">&lt;expr&gt;</span> <span style="color: #8cf8f7;"><span style="color: #e0e2ea;">&lt;</span>C-d<span style="color: #e0e2ea;">&gt;</span></span> <span style="color: #8cf8f7;">compe#scroll</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #b3f6c0;">&#39;delta&#39;</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">-4</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>]]</span>
</div><div class="line" data-line="34">
</div><div class="line" data-line="35">  <span style="color: #9b9ea4;">-- tell nvim-cmp about our desired capabilities</span>
</div><div class="line" data-line="36">  <span style="color: #8cf8f7;">require</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;cmp_nvim_lsp&quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">update_capabilities</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">capabilities</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="37"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="38">
</div><div class="line" data-line="39"><span style="color: #9b9ea4;">-- Finally, let&#39;s initialize the Elixir language server</span>
</div><div class="line" data-line="40">
</div><div class="line" data-line="41"><span style="color: #9b9ea4;">-- Replace the following with the path to your installation</span>
</div><div class="line" data-line="42"><span style="color: #e0e2ea; font-weight: bold;">local</span> <span style="color: #e0e2ea;">path_to_elixirls</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">vim</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">fn</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">expand</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;~/.cache/nvim/lspconfig/elixirls/elixir-ls/release/language_server.sh&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="43">
</div><div class="line" data-line="44"><span style="color: #e0e2ea;">lspconfig</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">elixirls</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">setup</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="45">  <span style="color: #a6dbff;">cmd</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">&lbrace;</span><span style="color: #e0e2ea;">path_to_elixirls</span><span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="46">  <span style="color: #a6dbff;">capabilities</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">capabilities</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="47">  <span style="color: #a6dbff;">on_attach</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">on_attach</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="48">  <span style="color: #a6dbff;">settings</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="49">    <span style="color: #a6dbff;">elixirLS</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="50">      <span style="color: #9b9ea4;">-- I choose to disable dialyzer for personal reasons, but</span>
</div><div class="line" data-line="51">      <span style="color: #9b9ea4;">-- I would suggest you also disable it unless you are well</span>
</div><div class="line" data-line="52">      <span style="color: #9b9ea4;">-- acquainted with dialzyer and know how to use it.</span>
</div><div class="line" data-line="53">      <span style="color: #a6dbff;">dialyzerEnabled</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">false</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="54">      <span style="color: #9b9ea4;">-- I also choose to turn off the auto dep fetching feature.</span>
</div><div class="line" data-line="55">      <span style="color: #9b9ea4;">-- It often get&#39;s into a weird state that requires deleting</span>
</div><div class="line" data-line="56">      <span style="color: #9b9ea4;">-- the .elixir_ls directory and restarting your editor.</span>
</div><div class="line" data-line="57">      <span style="color: #a6dbff;">fetchDeps</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">false</span>
</div><div class="line" data-line="58">    <span style="color: #8cf8f7;">&rbrace;</span>
</div><div class="line" data-line="59">  <span style="color: #8cf8f7;">&rbrace;</span>
</div><div class="line" data-line="60"><span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<p>Elixir LS should be all set up now! Let's test it out by seeing if autocompletion, documentation on hover, and go to definition is working.</p>
<p><img src="https://res.cloudinary.com/mhanberg/image/upload/v1621999434/elixir-ls-demo.gif" alt="Gif demonstration of autocomplete, documentation on hover, and go to definition" /></p>
<h2><a href="#integrating-credo" aria-hidden="true" class="anchor" id="integrating-credo"></a>Integrating Credo</h2>
<p>I decided to completely remove <a href="https://github.com/dense-analysis/ale">ALE</a>, so I was wondering how I might get linters and formatters like credo and prettier hooked back in.</p>
<p>Luckily, there are a few projects that implement a language server for the purpose of running these tools for you. I am currently using <a href="https://github.com/mattn/efm-langserver">efm-langserver</a>.</p>
<p>I install efm with brew.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ brew install efm-langserver
</div></code></pre>
<p>Once that is installed, let's hook it up to Neovim.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-lua" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">lspconfig</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">efm</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">setup</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">&lbrace;</span>
</div><div class="line" data-line="2">  <span style="color: #a6dbff;">capabilities</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">capabilities</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">  <span style="color: #a6dbff;">on_attach</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">on_attach</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">  <span style="color: #a6dbff;">filetypes</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">&lbrace;</span><span style="color: #b3f6c0;">&quot;elixir&quot;</span><span style="color: #8cf8f7;">&rbrace;</span>
</div><div class="line" data-line="5"><span style="color: #8cf8f7;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<p>And last, we need to teach efm how to speak Credo. Create a new file at <code>~/.config/efm-langserver/config.yaml</code>. Please note that we need to run Credo with <code>MIX_ENV=test</code> or else it's going to mess with Phoenix code reloading.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-yaml" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #a6dbff;">version</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">2</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #a6dbff;">tools</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="4">  <span style="color: #a6dbff;">mix_credo</span><span style="color: #e0e2ea;">:</span> <span style="color: #8cf8f7;">&amp;</span><span style="color: #e0e2ea; font-weight: bold;">mix_credo</span>
</div><div class="line" data-line="5">    <span style="color: #a6dbff;">lint-command</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">&quot;MIX_ENV=test mix credo suggest --format=flycheck --read-from-stdin $&lbrace;INPUT&rbrace;&quot;</span>
</div><div class="line" data-line="6">    <span style="color: #a6dbff;">lint-stdin</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">true</span>
</div><div class="line" data-line="7">    <span style="color: #a6dbff;">lint-formats</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="8">      <span style="color: #e0e2ea;">-</span> <span style="color: #b3f6c0;">&#39;%f:%l:%c: %t: %m&#39;</span>
</div><div class="line" data-line="9">      <span style="color: #e0e2ea;">-</span> <span style="color: #b3f6c0;">&#39;%f:%l: %t: %m&#39;</span>
</div><div class="line" data-line="10">    <span style="color: #a6dbff;">lint-category-map</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="11">      <span style="color: #a6dbff;">R</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">N</span>
</div><div class="line" data-line="12">      <span style="color: #a6dbff;">D</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">I</span>
</div><div class="line" data-line="13">      <span style="color: #a6dbff;">F</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">E</span>
</div><div class="line" data-line="14">      <span style="color: #a6dbff;">W</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">W</span>
</div><div class="line" data-line="15">    <span style="color: #a6dbff;">root-markers</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="16">      <span style="color: #e0e2ea;">-</span> <span style="color: #b3f6c0;">mix.lock</span>
</div><div class="line" data-line="17">      <span style="color: #e0e2ea;">-</span> <span style="color: #b3f6c0;">mix.exs</span>
</div><div class="line" data-line="18">
</div><div class="line" data-line="19"><span style="color: #a6dbff;">languages</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="20">  <span style="color: #a6dbff;">elixir</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="21">    <span style="color: #e0e2ea;">-</span> <span style="color: #a6dbff;">&lt;&lt;</span><span style="color: #e0e2ea;">:</span> <span style="color: #8cf8f7;">*</span><span style="color: #e0e2ea; font-weight: bold;">mix_credo</span>
</div></code></pre>
<p>Now you should be seeing Credo checks showing up inside Neovim.</p>
<h2><a href="#my-setup" aria-hidden="true" class="anchor" id="my-setup"></a>My Setup</h2>
<p>If you would like to check out my actual dotfiles, feel free to check them out on <a href="https://github.com/mhanberg/.dotfiles">GitHub</a>.</p>
<p>I've extracted quite a few helper modules and functions that make organizing my plugins a little easier.</p>
<p>Enjoy!</p> ]]></description>
    </item>
    <item>
       <title>Announcing Temple v0.6!</title>
       <link>https://www.mitchellhanberg.com/announcing-temple-v06/</link>
       <pubDate>Tue, 25 May 2021 10:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/announcing-temple-v06/</guid>
       <description><![CDATA[ <p>v0.6 of Temple has been released! 🎉</p>
<p>This release is the result of a few architecture changes that allows for faster rendering as well as many new features.</p>
<p><em>If you'd like to follow along with the code in this article, it's available <a href="https://github.com/mhanberg/temple_example">here</a></em>.</p>
<h2><a href="#compile-time-output-of-eex" aria-hidden="true" class="anchor" id="compile-time-output-of-eex"></a>Compile time output of EEx</h2>
<p>Temple now outputs EEx at compile time, allowing us to piggy back on the power of existing EEx Engines, such as the engines included by the <a href="https://phoenixframework.org">Phoenix Framework</a>. If you're using Temple with Phoenix, your templates should now be just as fast as the standard Phoenix templates, since the EEx output of Temple is piped right into the Phoenix template engine.</p>
<p>Compiling to EEx also means that Temple is now compatible with <a href="https://github.com/phoenixframework/phoenix_live_view">Phoenix LiveView</a>! This is made possible by the same process outlined above, without having to re-invent any wheels.</p>
<h2><a href="#improved-component-api" aria-hidden="true" class="anchor" id="improved-component-api"></a>Improved Component API</h2>
<p>The current component API is module driven and comes with support for "slots".</p>
<p>Let's write a <code>Card</code> component to see how this all works.</p>
<p>A Temple component begins with importing the <code>Temple.Component</code> module, which brings us the <code>render/1</code> and <code>defcomp/2</code> macros.</p>
<h3><a href="#defcomp" aria-hidden="true" class="anchor" id="defcomp"></a>defcomp</h3>
<p><code>defcomp</code> allows us to create a basic component within any module. The only limitation is that you can't define any functions inside of it, as the body of the macro is the markup of the component. Here we are creating several more components using <code>defcomp</code>, but you can use this macro anywhere. Under the hood, it defines a Temple component module.</p>
<h3><a href="#render" aria-hidden="true" class="anchor" id="render"></a>render</h3>
<p><code>render</code> is where your component markup will go. Since Temple components are basically the same as Phoenix views, this macro expands into a render function that takes assigns, which you can access like <code>@my_assign</code> just like you would do in a normal EEx template.</p>
<h3><a href="#slot" aria-hidden="true" class="anchor" id="slot"></a>slot</h3>
<p>Inside each component definition, you'll see the usage of <code>slot :default</code>. This is the proper way to render the inner content of the component that is defined at the call site. <code>slot</code> is more like a <em>keyword</em>, as it is not a macro or a function.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">TempleExampleWeb.Components.Card</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Temple.Component</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">defcomp</span> <span style="color: #e0e2ea;">Header</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">    <span style="color: #8cf8f7;">header</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;p-4 border-b border-gray-300 bg-gray-50 rounded-t-lg&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="6">      <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;flex items-center space-x-4&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="7">        <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:default</span>
</div><div class="line" data-line="8">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="9">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="10">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="11">
</div><div class="line" data-line="12">  <span style="color: #8cf8f7;">defcomp</span> <span style="color: #e0e2ea;">Body</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="13">    <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;p-4 border-b border-gray-300&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="14">      <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:default</span>
</div><div class="line" data-line="15">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="16">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="17">
</div><div class="line" data-line="18">  <span style="color: #8cf8f7;">defcomp</span> <span style="color: #e0e2ea;">Footer</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="19">    <span style="color: #8cf8f7;">footer</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;bg-gray-50 rounded-b-lg p-4&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="20">      <span style="color: #8cf8f7;">ul</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;flex items-center&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="21">        <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:default</span>
</div><div class="line" data-line="22">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="23">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="24">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="25">
</div><div class="line" data-line="26">  <span style="color: #8cf8f7;">render</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="27">    <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;bg-white w-full flex flex-col rounded-lg shadow-lg&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="28">      <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:default</span>
</div><div class="line" data-line="29">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="30">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="31"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>Here we've defined a <code>Card</code> component as well as <code>Card.Header</code>, <code>Card.Body</code>, and <code>Card.Footer</code> components. Now we can easily compose these components together to create a few cards.</p>
<h3><a href="#c" aria-hidden="true" class="anchor" id="c"></a>c</h3>
<p>We render a component calling the <code>c</code> <em>keyword</em> with a component module. I decided to keep this keyword as short as I could to make using components as ergonomic as possible. In the beginning, I wanted to be able to render them <em>without</em> any keyword, but calling a module like a function is not valid Elixir syntax, so I couldn't do that.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;grid grid-cols-3 gap-4 p-4 items-start&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">user</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">users</span></span></span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">c</span> <span style="color: #e0e2ea;">Card</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="4">      <span style="color: #8cf8f7;">c</span> <span style="color: #e0e2ea;">Card.Header</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">        <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;w-10 rounded-full overflow-hidden flex-shrink-0&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="6">          <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;aspect-w-1 aspect-h-1&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="7">            <span style="color: #8cf8f7;">img</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;object-cover&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">src: </span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">avatar</span>
</div><div class="line" data-line="8">          <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="9">        <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11">        <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">do: </span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span>
</div><div class="line" data-line="12">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="13">
</div><div class="line" data-line="14">      <span style="color: #8cf8f7;">c</span> <span style="color: #e0e2ea;">Card.Body</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="15">        <span style="color: #8cf8f7;">text_to_html</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">bio</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">attributes: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;first:mt-0 mt-2&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="16">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="17">
</div><div class="line" data-line="18">      <span style="color: #e0e2ea; font-weight: bold;">unless</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">empty?</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">socials</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="19">        <span style="color: #8cf8f7;">c</span> <span style="color: #e0e2ea;">Card.Footer</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="20">          <span style="color: #8cf8f7;">c</span> <span style="color: #e0e2ea;">LinkList</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">socials: </span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">socials</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="21">            <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:link</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">text: </span><span style="color: #e0e2ea;">text</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">url: </span><span style="color: #e0e2ea;">url</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="22">              <span style="color: #8cf8f7;">c</span> <span style="color: #e0e2ea;">LinkList.Item</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">url: </span><span style="color: #e0e2ea;">url</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="23">                <span style="color: #e0e2ea;">text</span>
</div><div class="line" data-line="24">              <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="25">            <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="26">          <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="27">        <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="28">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="29">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="30">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="31"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p><img src="https://res.cloudinary.com/mhanberg/image/upload/v1621913949/Screen_Shot_2021-05-24_at_11.41.34_PM.png" alt="Screenshot of 3 cards created using the above code snippet" /></p>
<p>The <code>Card</code> related components all called <code>slot :default</code> to render their inner content, but as we can see inside our usage of <code>LinkList</code> within the <code>Card.Footer</code> component, we call <code>slot :link, %&lbrace;text: text, url: url&rbrace;</code>, why is that??</p>
<p>I'm glad you asked. The key feature of slots in my eyes is the ability for a component to pass data back into the scope of the caller.</p>
<p>Before, we were <em>rendering</em> a slot, whereas now we are <em>defining</em> a slot.</p>
<p>It's pretty easy to think of this in the context of a function that takes an anonymous function as an argument, like how you usually render a form in a Phoenix project.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">form_for</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">user</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">Routes</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">user_path</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">conn</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:edit</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">user</span></span></span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">id</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">form</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="2">  <span style="color: #9b9ea4;"># markup and functions that use the form</span>
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<p>If we were to make a form <em>component</em>, we could use a slot to do the same thing. You can see in these examples that we can pattern match on the assigns that are passed to the slot, allowing us to easily rename a variable if we want.</p>
<p>Here's how we might make a form component that is compatible with how we're supposed to write forms in LiveView.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">TempleExampleWeb.Components.Form</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Temple.Component</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">render</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">    <span style="color: #8cf8f7;">form_for</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">changeset</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">path</span></span></span><span style="color: #e0e2ea;">)</span>  
</div><div class="line" data-line="6">
</div><div class="line" data-line="7">    <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:form</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">form: </span><span style="color: #8cf8f7;">form_for</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">changeset</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">path</span></span></span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">    <span style="color: #b3f6c0;">&quot;&lt;/form&gt;&quot;</span>
</div><div class="line" data-line="10">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="11"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="12">
</div><div class="line" data-line="13"><span style="color: #9b9ea4;"># usage</span>
</div><div class="line" data-line="14">
</div><div class="line" data-line="15"><span style="color: #e0e2ea; font-weight: bold;">alias</span> <span style="color: #e0e2ea;">TempleExampleWeb.Components.Form</span>
</div><div class="line" data-line="16">
</div><div class="line" data-line="17"><span style="color: #8cf8f7;">c</span> <span style="color: #e0e2ea;">Form</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">changeset: </span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">user</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">path: </span><span style="color: #e0e2ea;">Routes</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">user_path</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">conn</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:edit</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">user</span></span></span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">id</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="18">  <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:form</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">form: </span><span style="color: #e0e2ea;">f</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="19">    <span style="color: #9b9ea4;"># markup and functions that use the form</span>
</div><div class="line" data-line="20">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="21"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>The nice part of slots is that we can have more than one and control exactly where they are rendered inside the component. Let's take another look at our <code>Card.Header</code> component to see what I mean.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">defcomp</span> <span style="color: #e0e2ea;">Header</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">header</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;p-4 border-b border-gray-300 bg-gray-50 rounded-t-lg&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;flex items-center justify-between&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="4">      <span style="color: #8cf8f7;">div</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">        <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:left</span>
</div><div class="line" data-line="6">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8">      <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:default</span>
</div><div class="line" data-line="9">
</div><div class="line" data-line="10">      <span style="color: #8cf8f7;">div</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="11">        <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:right</span>
</div><div class="line" data-line="12">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="13">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="14">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="15"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>Now we can slot (😅) some markup into specific parts of the component.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">c</span> <span style="color: #e0e2ea;">Card.Header</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:left</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;w-10 rounded-full overflow-hidden&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="4">      <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;aspect-w-1 aspect-h-1&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">        <span style="color: #8cf8f7;">img</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;object-cover&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">src: </span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">avatar</span>
</div><div class="line" data-line="6">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="7">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="9">
</div><div class="line" data-line="10">  <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:right</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="11">    <span style="color: #e0e2ea;">SVG</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">verified_icon</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="12">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="13">
</div><div class="line" data-line="14">  <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">do: </span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span>
</div><div class="line" data-line="15"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<h2><a href="#whats-next" aria-hidden="true" class="anchor" id="whats-next"></a>What's Next?</h2>
<p>While Temple has certainly come a long way, there are still many improvements to be made! The ones that I can immediately think of include:</p>
<ul>
<li><a href="https://github.com/mhanberg/temple/issues/129">Fallback content for slots</a></li>
<li><a href="https://github.com/mhanberg/temple/issues/130">Allow multiple instances of a single slot name</a></li>
<li><a href="https://github.com/mhanberg/temple/issues/128">Use a component as a "typed" slot</a></li>
<li>Improve stacktraces. Currently if something goes wrong, you might see errors/warnings being rendered on the wrong lines.</li>
<li>Writing guides. The hex documentation is alright, but doesn't really teach you how to use Temple.</li>
<li>Write a "component library" a la something like <a href="https://react-bootstrap.github.io/">React Bootstrap</a></li>
<li>New logo 👨‍🎨</li>
</ul>
<hr />
<h2><a href="#related-reading" aria-hidden="true" class="anchor" id="related-reading"></a>Related Reading</h2>
<ul>
<li><a href="/temple-ast-and-protocols">Temple, AST, and Protocols</a></li>
<li><a href="/introducing-temple-an-elegant-html-library-for-elixir-and-phoenix/">Introducing Temple: An elegant HTML library for Elixir and Phoenix</a></li>
<li><a href="https://github.com/mhanberg/temple/releases/tag/v0.6.0">Release Notes</a></li>
</ul> ]]></description>
    </item>
    <item>
       <title>Temple, AST, and Protocols</title>
       <link>https://www.mitchellhanberg.com/temple-ast-and-protocols/</link>
       <pubDate>Mon, 12 Apr 2021 10:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/temple-ast-and-protocols/</guid>
       <description><![CDATA[ <p>As <a href="https://github.com/mhanberg/temple">Temple</a> has aged, my ambition for this little library has grown.</p>
<p>Temple started with the ability to produce HTML at runtime, but now includes:</p>
<ul>
<li>EEx output target</li>
<li>LiveView support (it's just EEx after all!)</li>
<li>Basic component functionality (essentially just partials)</li>
</ul>
<p>If my goals for this project are going to evolve, so does the code base. So far I've been able to accomplish this with a rather naive and imperative compilation process.</p>
<p>I figured that writing an actual abstract syntax tree (AST) as an intermediate format (IF) would be the next step, and along that journey I also found a nice use case for a <a href="https://elixir-lang.org/getting-started/protocols.html">protocol</a>.</p>
<p>Before we look at the new AST, let's go over how things used to work.</p>
<p>The previous method would recursively traverse Elixir AST, storing the collected tokens in a global buffer (backed by an <a href="https://hexdocs.pm/elixir/Agent.html">Agent</a>).</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">buffer</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">Agent</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">start_link</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea;">Utils</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">traverse</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">ast</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">buffer</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">markup</span> <span style="color: #e0e2ea;">=</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea;">buffer</span>
</div><div class="line" data-line="7">  <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Agent</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">get</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">buf</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">buf</span> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">reverse</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="9">  <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">join</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">\n</span>&quot;</span><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<p>The <code>Utils.traverse/2</code> function would call a certain parser based on the Elixir AST with which it was working. The parser that would be invoked for lines that include anonymous functions looked something like this.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #9b9ea4;">_do_and_else</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">args</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">=</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">args</span>
</div><div class="line" data-line="3">  <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Utils</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">split_args</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">args</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">func_arg</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">args2</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">Utils</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">split_on_fn</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">args</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">nil</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">func</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">arrow</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">arg</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">block</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">func_arg</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9"><span style="color: #e0e2ea;">Agent</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">update</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">buffer</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">buf</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="10">  <span style="color: #e0e2ea;">markup</span> <span style="color: #e0e2ea;">=</span> <span style="color: #b3f6c0;">&quot;&lt;%= &quot;</span> <span style="color: #e0e2ea;">&lt;&gt;</span>
</div><div class="line" data-line="11">           <span style="color: #8cf8f7;">to_string</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lt;&gt;</span>
</div><div class="line" data-line="12">           <span style="color: #b3f6c0;">&quot; &quot;</span> <span style="color: #e0e2ea;">&lt;&gt;</span>
</div><div class="line" data-line="13">           <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">args</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&amp;</span><span style="color: #e0e2ea;">Macro</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">to_string</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&amp;</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">join</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;, &quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lt;&gt;</span>
</div><div class="line" data-line="14">           <span style="color: #b3f6c0;">&quot;, &quot;</span> <span style="color: #e0e2ea;">&lt;&gt;</span>
</div><div class="line" data-line="15">           <span style="color: #8cf8f7;">to_string</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">func</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lt;&gt;</span>
</div><div class="line" data-line="16">           <span style="color: #b3f6c0;">&quot; &quot;</span> <span style="color: #e0e2ea;">&lt;&gt;</span>
</div><div class="line" data-line="17">           <span style="color: #8cf8f7;">to_string</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">arg</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lt;&gt;</span>
</div><div class="line" data-line="18">           <span style="color: #b3f6c0;">&quot; &quot;</span> <span style="color: #e0e2ea;">&lt;&gt;</span>
</div><div class="line" data-line="19">           <span style="color: #8cf8f7;">to_string</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">arrow</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lt;&gt;</span>
</div><div class="line" data-line="20">           <span style="color: #b3f6c0;">&quot; %&gt;&quot;</span>
</div><div class="line" data-line="21">
</div><div class="line" data-line="22">  <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">markup</span> <span style="color: #e0e2ea;">|</span> <span style="color: #e0e2ea;">buf</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="23"><span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="24">
</div><div class="line" data-line="25"><span style="color: #e0e2ea;">Agent</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">update</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">buf</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">\n</span>&quot;</span> <span style="color: #e0e2ea;">|</span> <span style="color: #e0e2ea;">buf</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="26">
</div><div class="line" data-line="27"><span style="color: #e0e2ea;">Utils</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">traverse</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">buffer</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">block</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="28">
</div><div class="line" data-line="29"><span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">any?</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">args2</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="30">  <span style="color: #e0e2ea;">post_fn_args</span> <span style="color: #e0e2ea;">=</span>
</div><div class="line" data-line="31">    <span style="color: #e0e2ea;">args2</span>
</div><div class="line" data-line="32">    <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">arg</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">Macro</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">to_string</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">arg</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="33">    <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">join</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;, &quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="34">
</div><div class="line" data-line="35">  <span style="color: #e0e2ea;">Agent</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">update</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">buf</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="36">    <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;&lt;% end, &quot;</span> <span style="color: #e0e2ea;">&lt;&gt;</span> <span style="color: #e0e2ea;">post_fn_args</span> <span style="color: #e0e2ea;">&lt;&gt;</span> <span style="color: #b3f6c0;">&quot; %&gt;&quot;</span> <span style="color: #e0e2ea;">|</span> <span style="color: #e0e2ea;">buf</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="37">  <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="38">
</div><div class="line" data-line="39">  <span style="color: #e0e2ea;">Agent</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">update</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">buf</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">\n</span>&quot;</span> <span style="color: #e0e2ea;">|</span> <span style="color: #e0e2ea;">buf</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="40"><span style="color: #e0e2ea; font-weight: bold;">else</span>
</div><div class="line" data-line="41">  <span style="color: #e0e2ea;">Agent</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">update</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">buf</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;&lt;% end %&gt;&quot;</span> <span style="color: #e0e2ea;">|</span> <span style="color: #e0e2ea;">buf</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="42">  <span style="color: #e0e2ea;">Agent</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">update</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">buf</span> <span style="color: #e0e2ea;">-&gt;</span> <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">\n</span>&quot;</span> <span style="color: #e0e2ea;">|</span> <span style="color: #e0e2ea;">buf</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="43"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>This code illustrates that I am compiling the Elixir AST into markup all in one pass and utilizing some global state to store the compiled markup.</p>
<p>Named Slots, the feature that I want to build before cutting the v0.6.0 release, would be extremely complex or impossible to write with the architecture I described above.</p>
<p>Let's discuss the AST and the benefits.</p>
<h2><a href="#temple-ast" aria-hidden="true" class="anchor" id="temple-ast"></a>Temple AST</h2>
<p>The AST follows a basic tree structure. Below I've demonstrated how some code you've probably written before would be represented by the AST.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">form_for</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">conn</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">Routes</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">widget_path</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">conn</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:create</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">f</span><span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">label</span> <span style="color: #e0e2ea;">f</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:name</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">span</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;text-bold&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="4">      <span style="color: #b3f6c0;">&quot;Name:&quot;</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7">    <span style="color: #8cf8f7;">text_input</span> <span style="color: #e0e2ea;">f</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:name</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">placeholder: </span><span style="color: #b3f6c0;">&quot;Name...&quot;</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #9b9ea4;"># parses into</span>
</div><div class="line" data-line="12">
</div><div class="line" data-line="13"><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">AnonymousFunctions</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="14">  <span style="color: #8cf8f7;">elixir_ast: </span><span style="color: #9b9ea4;"># the quoted expression from above,</span>
</div><div class="line" data-line="15">  <span style="color: #e0e2ea;">children</span>: <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="16">    <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">DoExpressions</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="17">      <span style="color: #8cf8f7;">elixir_ast: </span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:label</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:f</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">Elixir</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:name</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="18">      <span style="color: #8cf8f7;">children: </span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="19">        <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">NonvoidElementsAliases</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="20">          <span style="color: #8cf8f7;">name: </span><span style="color: #b3f6c0;">&quot;span&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="21">          <span style="color: #8cf8f7;">attrs: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;text-bold&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="22">          <span style="color: #8cf8f7;">children: </span><span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="23">            <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">Text</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">text: </span><span style="color: #b3f6c0;">&quot;Name:&quot;</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="24">          <span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="25">        <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="26">        <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">Default</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="27">          <span style="color: #8cf8f7;">elixir_ast:</span>
</div><div class="line" data-line="28"><span style="color: #8cf8f7;"></span>            <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:text_input</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:f</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">Elixir</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:name</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">placeholder: </span><span style="color: #b3f6c0;">&quot;Name...&quot;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="29">        <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="30">      <span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="31">    <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="32">  <span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="33"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<p>The biggest benefit to the AST is its role as an <em>intermediate format</em>. Since we've explored the entire AST, we can now run it through another step before generating the final output. The goal is to target EEx, but now that we have the IF, we could write a generator that targets ANSI sequences for a CLI or maybe even <a href="https://github.com/boydm/scenic">Scenic</a>!</p>
<p>This brings us to our next topic, protocols!</p>
<h2><a href="#protocols" aria-hidden="true" class="anchor" id="protocols"></a>Protocols</h2>
<p>The EEX generator step utilizes a <a href="https://elixir-lang.org/getting-started/protocols.html">protocol</a> to be able to compile Temple AST into an <code>iolist</code> that represents EEx.</p>
<p>Each AST module implements this protocol and this allows any protocol implementation to generate any child nodes it contains without concerning itself with the shape of the children.</p>
<p>The implementation for the <code>Text</code> node type is the easiest to understand.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">Temple.Parser.Text</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #9b9ea4;"># ...</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">defimpl</span> <span style="color: #e0e2ea;">Temple.Generator</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">to_eex</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">text: </span><span style="color: #e0e2ea;">text</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="6">      <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">text</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">\n</span>&quot;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="7">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>The benefit of using a protocol becomes clear when we look at the <code>NonvoidElementsAliases</code> implementation. The highlighted line belows shows how the protocol makes recursively compiling the AST super easy.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">Temple.Parser.NonvoidElementsAliases</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #9b9ea4;"># ...</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">defimpl</span> <span style="color: #e0e2ea;">Temple.Generator</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">to_eex</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">name: </span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">attrs: </span><span style="color: #e0e2ea;">attrs</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">children: </span><span style="color: #e0e2ea;">children</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="6">      <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="7">        <span style="color: #b3f6c0;">&quot;&lt;&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="8">        <span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="9">        <span style="color: #e0e2ea;">Temple.Parser.Utils</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">compile_attrs</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">attrs</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="10">        <span style="color: #b3f6c0;">&quot;&gt;<span style="color: #8cf8f7;">\n</span>&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="11">        <span style="color: #e0e2ea; font-weight: bold;">for</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">child</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">children</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #e0e2ea;">Temple.Generator</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">to_eex</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">child</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="12">        <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">\n</span>&lt;/&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="13">        <span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="14">        <span style="color: #b3f6c0;">&quot;&gt;&quot;</span>
</div><div class="line" data-line="15">      <span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="16">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="17">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="18"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>Since the implementation takes advantage of <code>iolist</code>s, we can easily compute the final markup without maintaining any state or dealing with cumbersome return values. Once <code>to_eex</code> returns, we just run that through <code>:erlang.iolist_to_binary/1</code> and we're good to go!</p>
<h2><a href="#whats-next" aria-hidden="true" class="anchor" id="whats-next"></a>What's Next</h2>
<p>With a proper AST in place, I can now move forward with the Named Slots API, which is the missing piece of the puzzle to make the Component API <em>really</em> slick.</p>
<p>Eventually, you should be able to write something like this. (The exact syntax is subject to change)</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">c</span> <span style="color: #e0e2ea;">Card</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">data: </span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">person</span></span></span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:header</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">data: </span><span style="color: #e0e2ea;">person</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="3">    <span style="color: #b3f6c0;">&quot;Full name: <span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">person</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">first_name</span><span style="color: #8cf8f7;">&rbrace;</span> <span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">person</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">last_name</span><span style="color: #8cf8f7;">&rbrace;</span>&quot;</span> 
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6">  <span style="color: #9b9ea4;"># some card body</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8">  <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:footer</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">data: </span><span style="color: #e0e2ea;">person</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="9">    <span style="color: #b3f6c0;">&quot;Find me on Twitter: &quot;</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11">    <span style="color: #8cf8f7;">a</span> <span style="color: #8cf8f7;">href: </span><span style="color: #b3f6c0;">&quot;https:twitter.com/<span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">person</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">socials</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">twitter</span><span style="color: #8cf8f7;">&rbrace;</span>&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="12">      <span style="color: #b3f6c0;">&quot;@&quot;</span> <span style="color: #e0e2ea;">&lt;&gt;</span> <span style="color: #e0e2ea;">person</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">socials</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">twitter</span>
</div><div class="line" data-line="13">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="14">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="15"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="16">
</div><div class="line" data-line="17"><span style="color: #8cf8f7;">c</span> <span style="color: #e0e2ea;">Card</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">data: </span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">company</span></span></span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="18">  <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:header</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">data: </span><span style="color: #e0e2ea;">company</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="19">    <span style="color: #b3f6c0;">&quot;Legal name: <span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">company</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span><span style="color: #8cf8f7;">&rbrace;</span>&quot;</span>
</div><div class="line" data-line="20">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="21">
</div><div class="line" data-line="22">  <span style="color: #9b9ea4;"># some card body</span>
</div><div class="line" data-line="23">
</div><div class="line" data-line="24">  <span style="color: #8cf8f7;">slot</span> <span style="color: #8cf8f7;">:footer</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">data: </span><span style="color: #e0e2ea;">company</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="25">    <span style="color: #b3f6c0;">&quot;Contact support at:&quot;</span>
</div><div class="line" data-line="26">
</div><div class="line" data-line="27">    <span style="color: #8cf8f7;">a</span> <span style="color: #8cf8f7;">href: </span><span style="color: #b3f6c0;">&quot;tel:&quot;</span> <span style="color: #e0e2ea;">&lt;&gt;</span> <span style="color: #e0e2ea;">company</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">phone_number</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="28">      <span style="color: #e0e2ea;">person</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">phone_number</span>
</div><div class="line" data-line="29">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="30">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="31"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>See you next time!</p> ]]></description>
    </item>
    <item>
       <title>How to Deploy a Phoenix App to Gigalixir in 20 Minutes</title>
       <link>https://www.mitchellhanberg.com/how-to-deploy-a-phoenix-app-to-gigalixir-in-20-minutes/</link>
       <pubDate>Fri, 06 Nov 2020 09:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/how-to-deploy-a-phoenix-app-to-gigalixir-in-20-minutes/</guid>
       <description><![CDATA[ <div class="flex justify-center py-8">
  <iframe width="560" height="315" src="https://www.youtube.com/embed/z2nko60GcGo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p>This is a quick screencast to demonstrate how easy it is to deploy an Elixir application to <a href="https://gigalixir.com">Gigalixir</a>, a hosting platform built specifically for Elixir.</p>
<p>I included a condensed text version of the video for those who prefer it, but you can find the full guide <a href="https://hexdocs.pm/phoenix/overview.html#content">here</a>.</p>
<h2><a href="#create-the-phoenix-app" aria-hidden="true" class="anchor" id="create-the-phoenix-app"></a>Create the Phoenix App</h2>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1"># install the generator
</div><div class="line" data-line="2">$ mix archive.install hex phx_new 1.5.6
</div><div class="line" data-line="3">
</div><div class="line" data-line="4"># generate the project
</div><div class="line" data-line="5">$ mix phx.new foobar
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"># cd into the directory
</div><div class="line" data-line="8">$ cd foobar
</div><div class="line" data-line="9">
</div><div class="line" data-line="10"># create our database
</div><div class="line" data-line="11">$ mix ecto.create
</div><div class="line" data-line="12">
</div><div class="line" data-line="13"># start the server
</div><div class="line" data-line="14">$ mix phx.server
</div><div class="line" data-line="15">
</div><div class="line" data-line="16"># commit your code to git
</div><div class="line" data-line="17">$ git init &amp;&amp; git commit -am &#39;Initial Commit&#39;
</div></code></pre>
<h2><a href="#prod-configuration" aria-hidden="true" class="anchor" id="prod-configuration"></a>Prod Configuration</h2>
<p>You will have to adjust some prod configuration for our app to work on Gigalixir. Let's make the following adjustments to the <code>config/prod.exs</code> file.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">  config :foobar, FoobarWeb.Endpoint,
</div><div class="line" data-line="2"><span style="color: #ffc0b9;"><span style="color: #8cf8f7;">-</span>   url: [host: &quot;example.com&quot;, port: 80],</span>
</div><div class="line" data-line="3"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   url: [host: &quot;your-app-name.gigalixirapp.com&quot;, port: 443],</span>
</div><div class="line" data-line="4"><span style="color: #ffc0b9;"><span style="color: #8cf8f7;">-</span>   cache_static_manifest: &quot;priv/static/cache_manifest.json&quot;</span>
</div><div class="line" data-line="5"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   cache_static_manifest: &quot;priv/static/cache_manifest.json&quot;,</span>
</div><div class="line" data-line="6"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   force_ssl: [rewrite_on: [:x_forwarded_proto]]</span>
</div><div class="line" data-line="7"> 
</div><div class="line" data-line="8"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span> config :foobar, Foobar.Repo,</span>
</div><div class="line" data-line="9"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   ssl: true</span>
</div></code></pre>
<h2><a href="#deploy-to-gigalixir" aria-hidden="true" class="anchor" id="deploy-to-gigalixir"></a>Deploy to Gigalixir</h2>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1"># install gigalixir (using Homebrew)
</div><div class="line" data-line="2">$ brew tap gigalixir/brew &amp;&amp; brew install gigalixir
</div><div class="line" data-line="3">
</div><div class="line" data-line="4"># sign up for gigalixir
</div><div class="line" data-line="5">$ gigalixir signup
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"># log in to gigalixir
</div><div class="line" data-line="8">$ gigalixir login
</div><div class="line" data-line="9">
</div><div class="line" data-line="10"># create the app
</div><div class="line" data-line="11">$ gigalixir create
</div><div class="line" data-line="12">
</div><div class="line" data-line="13"># set up your buildpacks
</div><div class="line" data-line="14">$ echo &quot;elixir_version=1.10.3&quot; &gt; elixir_buildpack.config
</div><div class="line" data-line="15">$ echo &quot;erlang_version=22.3&quot; &gt;&gt; elixir_buildpack.config
</div><div class="line" data-line="16">$ echo &quot;node_version=12.16.3&quot; &gt; phoenix_static_buildpack.config
</div><div class="line" data-line="17">
</div><div class="line" data-line="18"># commit those changes
</div><div class="line" data-line="19">$ git commit -am &#39;Set up gigalixir buildpacks&#39;
</div><div class="line" data-line="20">
</div><div class="line" data-line="21"># create your prod database
</div><div class="line" data-line="22">$ gigalixir pg:create --free
</div><div class="line" data-line="23">
</div><div class="line" data-line="24"># deploy your app
</div><div class="line" data-line="25">$ git push gigalixir main
</div><div class="line" data-line="26">
</div><div class="line" data-line="27"># open your prod app
</div><div class="line" data-line="28">$ gigalixir open
</div></code></pre> ]]></description>
    </item>
    <item>
       <title>Better Terminal Git Diffs</title>
       <link>https://www.mitchellhanberg.com/better-terminal-git-diffs/</link>
       <pubDate>Wed, 12 Aug 2020 09:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/better-terminal-git-diffs/</guid>
       <description><![CDATA[ <p>Normally, our terminal git diffs look like this.</p>
<p><img src="https://res.cloudinary.com/mhanberg/image/upload/v1597177346/normal-git-diff.png" alt="normal git diff" /></p>
<p>Let's improve our experience by adding syntax highlighting!</p>
<p>To accomplish this, we're going to replace the pager that git uses with a tool called <a href="https://github.com/dandavison/delta">delta</a>. Add the following to <code>~/.gitconfig</code>.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">[core]
</div><div class="line" data-line="2">  pager = &quot;delta&quot;
</div></code></pre>
<p>Let's see what our git diffs look like now.</p>
<p><img src="https://res.cloudinary.com/mhanberg/image/upload/v1597174958/git-diff-delta.png" alt="git diff using delta as the pager" /></p>
<p>This looks great, but I think we can do even better. Let's configure <code>delta</code> to use the same theme as our terminal. <code>delta</code> uses <a href="https://github.com/sharkdp/bat/">bat</a> for syntax highlighting, so we can use any theme that <code>bat</code> can use.</p>
<p>Grab your theme (you'll have to find a TextMate/Sublime Text version of it), copy it into <code>~/.config/bat/themes</code>, and add the following option to your pager configuration.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">[core]
</div><div class="line" data-line="2">  pager = &quot;delta --theme=&#39;Forest Night&#39;&quot;
</div></code></pre>
<p>Now, the highlighting blends seamlessly with the rest of my development experience.</p>
<p><img src="https://res.cloudinary.com/mhanberg/image/upload/v1597175033/delta-git-diff-with-theme.png" alt="git diff using delta as the pager" /></p>
<p>One last improvement you can make is to use the same diff colors that your theme uses. I copied them from the theme file and added the following options to my pager configuration.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">[core]
</div><div class="line" data-line="2">  pager = &quot;delta --plus-color=\&quot;#333B2F\&quot; --minus-color=\&quot;#382F32\&quot; --theme=&#39;Forest Night&#39;&quot;
</div></code></pre>
<p><img src="https://res.cloudinary.com/mhanberg/image/upload/v1597175171/delta-git-diff-with-theme-with-diff-color.png" alt="git diff using delta as the pager" /></p>
<p>Okay, now that our diffs look good, we can finally get back to work 😄.</p> ]]></description>
    </item>
    <item>
       <title>CI/CD with Phoenix, GitHub Actions, and Gigalixir</title>
       <link>https://www.mitchellhanberg.com/ci-cd-with-phoenix-github-actions-and-gigalixir/</link>
       <pubDate>Tue, 03 Mar 2020 09:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/ci-cd-with-phoenix-github-actions-and-gigalixir/</guid>
       <description><![CDATA[ <p>I like to start my side projects by immediately configuring them with a CI pipeline and automatic deployments. I normally go with Heroku, but Heroku has some drawbacks when deploying Elixir applications.</p>
<p>Luckily, another PaaS solution exists and is designed specifically for Elixir, <a href="https://www.gigalixir.com">Gigalixir</a>! Let's build a project called "Pipsqueak" and learn how to automatically test and deploy a new Phoenix application to Gigalixir.</p>
<p><strong>Note</strong>: This article will assume that you already know how to install Elixir/Erlang and will reference tools like <a href="https://github.com/github/hub">hub</a> that I actually used while implementing the application that I describe below.</p>
<h2><a href="#create-a-new-phoenix-project" aria-hidden="true" class="anchor" id="create-a-new-phoenix-project"></a>Create a new Phoenix project</h2>
<p>Our first step is to create a new Phoenix application. We'll then immediately initialize our <code>git</code> repository and push it up to GitHub.</p>
<p>I like to make the first commit right after generating the project so you don't get lost when making your initial changes. This way, when you look at the second commit, you'll see the first changes that the developer made.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ mix phx.new pipsqueak &amp;&amp; cd pipsqueak
</div><div class="line" data-line="2">
</div><div class="line" data-line="3">$ git add . &amp;&amp; git commit -m &quot;Initial Commit&quot;
</div><div class="line" data-line="4">
</div><div class="line" data-line="5">$ hub create
</div><div class="line" data-line="6">
</div><div class="line" data-line="7">$ git push -u origin master
</div></code></pre>
<h2><a href="#switch-to-yarn" aria-hidden="true" class="anchor" id="switch-to-yarn"></a>Switch to Yarn</h2>
<p>I like to use Yarn instead of NPM to manage my JavaScript packages, so we'll re-install the packages using Yarn to create our <code>yarn.lock</code> file.</p>
<p>If you don't use Yarn, you can skip this step.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ rm assets/package-lock.json
</div><div class="line" data-line="2">
</div><div class="line" data-line="3">$ (cd assets &amp;&amp; yarn install)
</div><div class="line" data-line="4">
</div><div class="line" data-line="5">$ git add . &amp;&amp; git commit -m &quot;Switch to Yarn&quot;
</div></code></pre>
<h2><a href="#continuous-integration-with-github-actions" aria-hidden="true" class="anchor" id="continuous-integration-with-github-actions"></a>Continuous Integration with GitHub Actions</h2>
<p><a href="https://www.github.com/actions">GitHub Actions</a> is a new product by GitHub that is used to run arbitrary workflows and CI pipelines in response to events emitted by GitHub. Let's run our new test suite by implementing a Workflow.</p>
<p>Our workflow will run on every push, check that our code is formatted, and run our test suite.</p>
<p>Notable pieces of this workflow are:</p>
<ul>
<li>We use the matrix strategy even though we are only using a single combination. This way we can use our version in our cache keys, so that we update cache will invalidate when we inevitably update our language versions.</li>
<li>We boot up our Postgres server as a service container. Make sure the user/password/db match your application's test config.</li>
<li>We cache our Elixir and JavaScript dependencies so that we only install them if we really have to.</li>
<li>We cache our Elixir build so that the compiler only has to compile the files that have changed.</li>
</ul>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1"># .github/workflows/verify.yml
</div><div class="line" data-line="2">
</div><div class="line" data-line="3">on: push
</div><div class="line" data-line="4">
</div><div class="line" data-line="5">jobs:
</div><div class="line" data-line="6">  verify:
</div><div class="line" data-line="7">    runs-on: ubuntu-latest
</div><div class="line" data-line="8">    strategy:
</div><div class="line" data-line="9">      matrix:
</div><div class="line" data-line="10">        otp: [22.2.7]
</div><div class="line" data-line="11">        elixir: [1.10.1]
</div><div class="line" data-line="12">
</div><div class="line" data-line="13">    services:
</div><div class="line" data-line="14">      db:
</div><div class="line" data-line="15">        image: postgres:12
</div><div class="line" data-line="16">        env:
</div><div class="line" data-line="17">          POSTGRES_USER: postgres
</div><div class="line" data-line="18">          POSTGRES_PASSWORD: postgres
</div><div class="line" data-line="19">          POSTGRES_DB: pipsqueak_test
</div><div class="line" data-line="20">        ports: [&#39;5432:5432&#39;]
</div><div class="line" data-line="21">        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
</div><div class="line" data-line="22">
</div><div class="line" data-line="23">    steps:
</div><div class="line" data-line="24">      - uses: actions/checkout@v2
</div><div class="line" data-line="25">      - uses: actions/setup-elixir@v1
</div><div class="line" data-line="26">        with:
</div><div class="line" data-line="27">          otp-version: $&lbrace;&lbrace; matrix.otp &rbrace;&rbrace;
</div><div class="line" data-line="28">          elixir-version: $&lbrace;&lbrace; matrix.elixir &rbrace;&rbrace;
</div><div class="line" data-line="29">
</div><div class="line" data-line="30">      - name: Setup Node
</div><div class="line" data-line="31">        uses: actions/setup-node@v1
</div><div class="line" data-line="32">        with:
</div><div class="line" data-line="33">          node-version: 13.8.0
</div><div class="line" data-line="34">
</div><div class="line" data-line="35">      - uses: actions/cache@v1
</div><div class="line" data-line="36">        id: deps-cache
</div><div class="line" data-line="37">        with:
</div><div class="line" data-line="38">          path: deps
</div><div class="line" data-line="39">          key: $&lbrace;&lbrace; runner.os &rbrace;&rbrace;-mix-$&lbrace;&lbrace; hashFiles(format(&#39;&lbrace;0&rbrace;&lbrace;1&rbrace;&#39;, github.workspace, &#39;/mix.lock&#39;)) &rbrace;&rbrace;
</div><div class="line" data-line="40">
</div><div class="line" data-line="41">      - uses: actions/cache@v1
</div><div class="line" data-line="42">        id: build-cache
</div><div class="line" data-line="43">        with:
</div><div class="line" data-line="44">          path: _build
</div><div class="line" data-line="45">          key: $&lbrace;&lbrace; runner.os &rbrace;&rbrace;-build-$&lbrace;&lbrace; matrix.otp &rbrace;&rbrace;-$&lbrace;&lbrace; matrix.elixir &rbrace;&rbrace;-$&lbrace;&lbrace; hashFiles(format(&#39;&lbrace;0&rbrace;&lbrace;1&rbrace;&#39;, github.workspace, &#39;/mix.lock&#39;)) &rbrace;&rbrace;
</div><div class="line" data-line="46">
</div><div class="line" data-line="47">      - name: Find yarn cache location
</div><div class="line" data-line="48">        id: yarn-cache
</div><div class="line" data-line="49">        run: echo &quot;::set-output name=dir::$(yarn cache dir)&quot;
</div><div class="line" data-line="50">
</div><div class="line" data-line="51">      - uses: actions/cache@v1
</div><div class="line" data-line="52">        with:
</div><div class="line" data-line="53">          path: $&lbrace;&lbrace; steps.yarn-cache.outputs.dir &rbrace;&rbrace;
</div><div class="line" data-line="54">          key: $&lbrace;&lbrace; runner.os &rbrace;&rbrace;-yarn-$&lbrace;&lbrace; hashFiles(&#39;**/yarn.lock&#39;) &rbrace;&rbrace;
</div><div class="line" data-line="55">          restore-keys: |
</div><div class="line" data-line="56">            $&lbrace;&lbrace; runner.os &rbrace;&rbrace;-yarn-
</div><div class="line" data-line="57">
</div><div class="line" data-line="58">      - name: Install deps
</div><div class="line" data-line="59">        run: |
</div><div class="line" data-line="60">          mix deps.get
</div><div class="line" data-line="61">          (cd assets &amp;&amp; yarn)
</div><div class="line" data-line="62">
</div><div class="line" data-line="63">      - run: mix format --check-formatted
</div><div class="line" data-line="64">      - run: mix test&lbrace;% endraw %&rbrace;
</div></code></pre>
<p>To test our workflow, let's commit and push to GitHub.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ git add . &amp;&amp; git commit -m &quot;Verify workflow&quot;
</div><div class="line" data-line="2">
</div><div class="line" data-line="3">$ git push origin master
</div></code></pre>
<h2><a href="#create-a-new-gigalixir-app" aria-hidden="true" class="anchor" id="create-a-new-gigalixir-app"></a>Create a new Gigalixir app</h2>
<p>Before proceeding with this section, please refer to the <a href="https://gigalixir.readthedocs.io/en/latest/main.html#getting-started-guide">Gigalixir Getting Started Guide</a> to create an account and install the <code>gigalixir</code> command line tool.</p>
<p>Our application will deploy using mix releases, so we need to add a <code>config/releases.exs</code> file. Gigalixir will detect this file and assume we are using a mix release.</p>
<p>The following will ensure that our server is started and URLs will be properly constructed.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;"># config/releases.exs</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Config</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #8cf8f7;">config</span> <span style="color: #8cf8f7;">:pipsqueak</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">PipsqueakWeb.Endpoint</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="6">  <span style="color: #8cf8f7;">server: </span><span style="color: #e0e2ea;">true</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">  <span style="color: #8cf8f7;">http: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">port: </span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:system</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;PORT&quot;</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="8">  <span style="color: #8cf8f7;">url: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">host: </span><span style="color: #e0e2ea;">System</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">get_env</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;APP_NAME&quot;</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lt;&gt;</span> <span style="color: #b3f6c0;">&quot;.gigalixirapp.com&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">port: </span><span style="color: #e0e2ea;">443</span><span style="color: #e0e2ea;">]</span>
</div></code></pre>
<p>Next, let's configure the Elixir, Erlang, Node, and Yarn versions to be used with our deployment. We'll use the latest version that are available at the time of writing.</p>
<p>Create the following files in the root of repository.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1"># elixir_buildpack.config
</div><div class="line" data-line="2">
</div><div class="line" data-line="3">elixir_version=1.10.2
</div><div class="line" data-line="4">erlang_version=22.2.8
</div><div class="line" data-line="5">
</div><div class="line" data-line="6"># phoenix_static_buildpack.config
</div><div class="line" data-line="7">
</div><div class="line" data-line="8">node_version=13.8.0
</div><div class="line" data-line="9">yarn_version=1.22.0
</div></code></pre>
<p>Time to commit the changes we've made.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ git add . &amp;&amp; git commit -m &quot;Configure for Gigalixir&quot;
</div></code></pre>
<p>Before we can deploy our application, we'll have to create an app and a database using the <code>gigalixir</code> command line tool.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ gigalixir create
</div><div class="line" data-line="2">
</div><div class="line" data-line="3">$ gigalixir pg:create --free
</div></code></pre>
<p>Now we have somewhere to deploy our code. Like Heroku, Gigalixir uses git for deployments. Running <code>gigalixir create</code> added a git remote named <code>gigalixir</code> and now we can push to it.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ git push gigalixir master
</div></code></pre>
<p>You should now be able to visit <code>&lt;your app name&gt;.gigalixirapp.com</code> and see our stock Phoenix application!</p>
<p><img src="https://res.cloudinary.com/mhanberg/image/upload/v1583205573/pipsqueak-gigalixirapp.png" alt="https://pipsqueak.gigalixirapp.com" /></p>
<p>While this is quick and easy when first starting out, it can be tedious and error prone in the long run.</p>
<p>This is where Gigalixir falls short compared to Heroku. Heroku connects to GitHub and automatically deploys when all of your <a href="https://developer.github.com/v3/checks/">checks</a> have passed. Checks are GitHub integrations that run some sort of test on your code and then either pass or fail.</p>
<p>Since we're using GitHub Actions, we have an entire marketplace of actions at our disposal. I saw that there wasn't an existing action for Gigalixir, so I decided to write my own!</p>
<h2><a href="#gigalixir-action" aria-hidden="true" class="anchor" id="gigalixir-action"></a>Gigalixir Action</h2>
<p>Check out the source code for this action <a href="https://github.com/mhanberg/gigalixir-action">here</a>.</p>
<p>This action has a few features:</p>
<ul>
<li>Deploy our application to Gigalixir.</li>
<li>Run our migrations upon a successful deployment.</li>
<li>If our migrations fail, it will rollback our deployment to the last version.</li>
</ul>
<p>Running migrations requires ssh access to the deployment, so before we can get started we need to generate a new public/private key pair, upload the public key to Gigalixir, and add the private key to GitHub as a secret.</p>
<p>Let's generate an SSH key pair (without a passphrase) called <code>gigalixir_rsa</code> in our current directory and add it to Gigalixir.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ ssh-keygen -t rsa -b 4096 -C &quot;Gigalixir SSH Key&quot;
</div><div class="line" data-line="2">
</div><div class="line" data-line="3">$ gigalixir account:ssh_keys:add &quot;$(cat gigalixir_rsa.pub)&quot;
</div><div class="line" data-line="4">
</div><div class="line" data-line="5">$ cat gigalixir_rsa | pbcopy
</div></code></pre>
<p>We also copied our private key to the clipboard so that we can add it as a GitHub secret.</p>
<p><img src="https://res.cloudinary.com/mhanberg/image/upload/v1583210165/pipsqueak-ssh-private-key.png" alt="Adding our SSH_PRIVATE_KEY as a secret" /></p>
<p>We also need to need to add our <code>GIGALIXIR_USERNAME</code> and <code>GIGALIXIR_PASSWORD</code> as secrets in the same way we added the <code>SSH_PRIVATE_KEY</code>.</p>
<p>For now you can use the same account that you used to create the app, but in the future you should create a separate user for added security.</p>
<p>We can now configure the <code>gigalixir-action</code> to deploy our code.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-yaml" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">&lbrace;</span>% raw %&rbrace;# .github/workflows/verify.yml
</div><div class="line" data-line="2">
</div><div class="line" data-line="3">jobs: 
</div><div class="line" data-line="4">  verify:
</div><div class="line" data-line="5">    ...
</div><div class="line" data-line="6">
</div><div class="line" data-line="7">  deploy:
</div><div class="line" data-line="8">    # only run this job if the verify job succeeds
</div><div class="line" data-line="9">    needs: verify
</div><div class="line" data-line="10">
</div><div class="line" data-line="11">    # only run this job if the workflow is running on the master branch
</div><div class="line" data-line="12">    if: github.ref == &#39;refs/heads/master&#39;
</div><div class="line" data-line="13">
</div><div class="line" data-line="14">    runs-on: ubuntu-latest
</div><div class="line" data-line="15">
</div><div class="line" data-line="16">    steps:
</div><div class="line" data-line="17">      - uses: actions/checkout@v2
</div><div class="line" data-line="18">        
</div><div class="line" data-line="19">        # actions/checkout@v2 only checks out the latest commit,
</div><div class="line" data-line="20">        # so we need to tell it to check out the entire master branch
</div><div class="line" data-line="21">        with:
</div><div class="line" data-line="22">          ref: master
</div><div class="line" data-line="23">          fetch-depth: 0
</div><div class="line" data-line="24">
</div><div class="line" data-line="25">      # configure the gigalixir-actions with our credentials and app name
</div><div class="line" data-line="26">      - uses: mhanberg/gigalixir-action@v0.1.0
</div><div class="line" data-line="27">        with:
</div><div class="line" data-line="28">          GIGALIXIR_USERNAME: $&lbrace;&lbrace; secrets.GIGALIXIR_USERNAME &rbrace;&rbrace;
</div><div class="line" data-line="29">          GIGALIXIR_PASSWORD: $&lbrace;&lbrace; secrets.GIGALIXIR_PASSWORD &rbrace;&rbrace;
</div><div class="line" data-line="30">          GIGALIXIR_APP: &lt;your app name&gt;
</div><div class="line" data-line="31">          SSH_PRIVATE_KEY: $&lbrace;&lbrace; secrets.SSH_PRIVATE_KEY &rbrace;&rbrace;&lbrace;% endraw %&rbrace;
</div></code></pre>
<p>Now all we have to do is commit and push our code!</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ git add . &amp;&amp; git commit -m &quot;Automatically deploy to Gigalixir&quot;
</div></code></pre>
<h2><a href="#whats-next" aria-hidden="true" class="anchor" id="whats-next"></a>What's next?</h2>
<p>Now there's nothing holding you back from moving forward with your next project 😄.</p>
<p>If this article has helped you deploy your latest project, please <a href="https://twitter.com/mitchhanberg">let me know</a> on Twitter!</p> ]]></description>
    </item>
    <item>
       <title>Setting up my new computer</title>
       <link>https://www.mitchellhanberg.com/setting-up-my-new-computer/</link>
       <pubDate>Mon, 02 Mar 2020 09:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/setting-up-my-new-computer/</guid>
       <description><![CDATA[ <p>I started a new job last week, so I naturally received a new computer that required some set up.</p>
<p>Usually this could take hours or even days, as there's always an app I forget to download or some random setting that I can't remember how to configure.</p>
<p>Luckily, I have automated most of the process! In my <a href="https://github.com/mhanberg/.dotfiles">dotfiles</a> repository, I wrote (some parts <a href="https://github.com/thoughtbot/laptop">borrowed</a>) a shell script to install all of my CLI and GUI tools for me, as well as configure my shell environment.</p>
<p>Installing everything still took at least an hour (the bulk of this is due to <code>brew</code> compiling packages from source), but at least I am able to do it by running only a few shell commands!</p>
<h2><a href="#my-process" aria-hidden="true" class="anchor" id="my-process"></a>My Process</h2>
<ul>
<li><a href="https://help.github.com/en/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent">Create a new ssh key</a> and upload the public key to my Github profile.</li>
<li>Install the Xcode Command Line Tools (<code>xcode-select --install</code>). This is necessary to be able to use <code>git</code>.</li>
<li>Clone my dotfiles into my home directory <code>git clone git@github.com:mhanberg/.dotfiles.git &amp;&amp; cd .dotfiles</code></li>
<li>Run the install script <code>./install</code></li>
<li>Watch as my computer magically bootstraps itself 😄.</li>
</ul>
<p>This is all made possible by <a href="https://brew.sh">Homebrew</a> and <a href="https://github.com/thoughtbot/rcm">rcm</a>.</p>
<h2><a href="#install" aria-hidden="true" class="anchor" id="install"></a>./install</h2>
<p>The <code>install</code> script installs Homebrew and then runs the command <code>brew bundle</code>. The <code>bundle</code> subcommand will install every package that is declared in the <code>Brewfile</code> that is located in the root of my dotfiles.</p>
<p>An awesome thing about Homebrew is that it can install GUI tools (Firefox, Postico, Slack) in addition to CLI developer tools.</p>
<p>You can hand-craft a <code>Brewfile</code> or generate one based on the current packages and casks you have installed using <code>brew bundle dump</code>.</p>
<p>So far I haven't found a good way to keep my <code>Brewfile</code> up to date. I think that there might be a way to hook into your shell to print a message every time you install something using <code>brew install</code>.</p>
<p>The script then switches the default shell to the <code>zsh</code> that is installed by <code>brew</code> (if it isn't already). <code>zsh</code> is the default shell on macOS now, but if you want to use the newest releases you'll want to install via <code>brew</code>.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">#! /usr/bin/env bash</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #8cf8f7;">set</span> <span style="color: #e0e2ea;">-e</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">!</span> <span style="color: #8cf8f7;">command</span> <span style="color: #e0e2ea;">-v</span> <span style="color: #e0e2ea;">brew</span> <span style="color: #e0e2ea;">&gt;</span><span style="color: #8cf8f7;">/dev/null</span><span style="color: #e0e2ea;">;</span> <span style="color: #e0e2ea; font-weight: bold;">then</span>
</div><div class="line" data-line="6">  <span style="color: #8cf8f7;">echo</span> <span style="color: #b3f6c0;">&quot;==&gt; Installing Homebrew ...&quot;</span>
</div><div class="line" data-line="7">  <span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">[[</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$</span><span style="color: #8cf8f7;">OSTYPE</span>&quot;</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">darwin<span style="color: #e0e2ea;">*</span></span> <span style="color: #e0e2ea;">]]</span><span style="color: #e0e2ea;">;</span> <span style="color: #e0e2ea; font-weight: bold;">then</span>
</div><div class="line" data-line="8">    <span style="color: #8cf8f7;">curl</span> <span style="color: #e0e2ea;">-fsS</span> <span style="color: #b3f6c0;">&#39;https://raw.githubusercontent.com/Homebrew/install/master/install&#39;</span> <span style="color: #e0e2ea;">|</span> <span style="color: #8cf8f7;">ruby</span>
</div><div class="line" data-line="9">  <span style="color: #e0e2ea; font-weight: bold;">else</span>
</div><div class="line" data-line="10">    <span style="color: #8cf8f7;">curl</span> <span style="color: #e0e2ea;">-fsSL</span> <span style="color: #b3f6c0;">&#39;https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh&#39;</span> <span style="color: #e0e2ea;">|</span> <span style="color: #8cf8f7;">sh</span> <span style="color: #e0e2ea;">-c</span>
</div><div class="line" data-line="11">  <span style="color: #e0e2ea; font-weight: bold;">fi</span>
</div><div class="line" data-line="12"><span style="color: #e0e2ea; font-weight: bold;">fi</span>
</div><div class="line" data-line="13">
</div><div class="line" data-line="14"><span style="color: #8cf8f7;">echo</span> <span style="color: #b3f6c0;">&quot;==&gt; Install Homebrew dependencies&quot;</span>
</div><div class="line" data-line="15"><span style="color: #8cf8f7;">brew</span> <span style="color: #e0e2ea;">bundle</span>
</div><div class="line" data-line="16">
</div><div class="line" data-line="17"><span style="color: #8cf8f7;">update_shell</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="18">  <span style="color: #e0e2ea; font-weight: bold;">local</span> <span style="color: #e0e2ea;">shell_path</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="19">  <span style="color: #e0e2ea;">shell_path</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$(</span><span style="color: #8cf8f7;">command</span> <span style="color: #e0e2ea;">-v</span> <span style="color: #e0e2ea;">zsh</span><span style="color: #8cf8f7;">)</span>&quot;</span>
</div><div class="line" data-line="20">
</div><div class="line" data-line="21">  <span style="color: #8cf8f7;">echo</span> <span style="color: #b3f6c0;">&quot;==&gt; Changing your shell to zsh ...&quot;</span>
</div><div class="line" data-line="22">  <span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">!</span> <span style="color: #8cf8f7;">grep</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$</span><span style="color: #e0e2ea;">shell_path</span>&quot;</span> <span style="color: #e0e2ea;">/etc/shells</span> <span style="color: #e0e2ea;">&gt;</span> <span style="color: #8cf8f7;">/dev/null</span> <span style="color: #e0e2ea;">2</span><span style="color: #e0e2ea;">&gt;&amp;</span><span style="color: #e0e2ea;">1</span> <span style="color: #e0e2ea;">;</span> <span style="color: #e0e2ea; font-weight: bold;">then</span>
</div><div class="line" data-line="23">    <span style="color: #8cf8f7;">echo</span> <span style="color: #b3f6c0;">&quot;==&gt; Adding &#39;<span style="color: #8cf8f7;">$</span><span style="color: #e0e2ea;">shell_path</span>&#39; to /etc/shells&quot;</span>
</div><div class="line" data-line="24">    <span style="color: #8cf8f7;">sudo</span> <span style="color: #e0e2ea;">sh</span> <span style="color: #e0e2ea;">-c</span> <span style="color: #b3f6c0;">&quot;echo <span style="color: #8cf8f7;">$</span><span style="color: #e0e2ea;">shell_path</span> &gt;&gt; /etc/shells&quot;</span>
</div><div class="line" data-line="25">  <span style="color: #e0e2ea; font-weight: bold;">fi</span>
</div><div class="line" data-line="26">  <span style="color: #8cf8f7;">sudo</span> <span style="color: #e0e2ea;">chsh</span> <span style="color: #e0e2ea;">-s</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$</span><span style="color: #e0e2ea;">shell_path</span>&quot;</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$</span><span style="color: #e0e2ea;">USER</span>&quot;</span>
</div><div class="line" data-line="27"><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="28">
</div><div class="line" data-line="29"><span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$</span><span style="color: #8cf8f7;">SHELL</span>&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">in</span>
</div><div class="line" data-line="30">  <span style="color: #8cf8f7;">*/zsh</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="31">    <span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">[</span> <span style="color: #b3f6c0;">&quot;<span style="color: #8cf8f7;">$(</span><span style="color: #8cf8f7;">command</span> <span style="color: #e0e2ea;">-v</span> <span style="color: #e0e2ea;">zsh</span><span style="color: #8cf8f7;">)</span>&quot;</span> <span style="color: #e0e2ea;">!=</span> <span style="color: #b3f6c0;">&#39;/usr/local/bin/zsh&#39;</span> <span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">;</span> <span style="color: #e0e2ea; font-weight: bold;">then</span>
</div><div class="line" data-line="32">      <span style="color: #8cf8f7;">echo</span> <span style="color: #b3f6c0;">&quot;==&gt; Updating shell to ZSH&quot;</span>
</div><div class="line" data-line="33">      <span style="color: #8cf8f7;">update_shell</span>
</div><div class="line" data-line="34">    <span style="color: #e0e2ea; font-weight: bold;">fi</span>
</div><div class="line" data-line="35">    <span style="color: #e0e2ea;">;;</span>
</div><div class="line" data-line="36">  <span style="color: #8cf8f7;">*</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="37">    <span style="color: #8cf8f7;">echo</span> <span style="color: #b3f6c0;">&quot;==&gt; Updating shell to ZSH&quot;</span>
</div><div class="line" data-line="38">    <span style="color: #8cf8f7;">update_shell</span>
</div><div class="line" data-line="39">    <span style="color: #e0e2ea;">;;</span>
</div><div class="line" data-line="40"><span style="color: #e0e2ea; font-weight: bold;">esac</span>
</div><div class="line" data-line="41">
</div><div class="line" data-line="42"><span style="color: #8cf8f7;">./rcup</span>
</div></code></pre>
<h2><a href="#rcup" aria-hidden="true" class="anchor" id="rcup"></a>./rcup</h2>
<p>And finally, we run my <code>rcup</code> wrapper script. This calls the <code>rcup</code> utility included with <code>rcm</code>, with a few options that I want to always use, such as ignoring certain files.</p>
<p><code>rcm</code> is a set of utilities that help manage your dotfiles using symlinks. When I call <code>rcup</code>, it will make symlinks in my home directory for any files (other than those I tell it to ignore) in <code>~/.dotfiles</code>.</p>
<p>When adding a new dotfile, I can either create it in my home directory and call <code>mkrc .my-new-dotfile</code> or I can create it in <code>~/dotfiles/</code> and run <code>~/.dotfiles/rcup</code> once more.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">#! /usr/bin/env bash</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #8cf8f7;">set</span> <span style="color: #e0e2ea;">-e</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #8cf8f7;">echo</span> <span style="color: #b3f6c0;">&quot;==&gt; Installing dotfiles&quot;</span>
</div><div class="line" data-line="6"><span style="color: #8cf8f7;">rcup</span> <span style="color: #e0e2ea;">-U</span> <span style="color: #e0e2ea;">Brewfile</span> <span style="color: #e0e2ea;">-x</span> <span style="color: #e0e2ea;">README.md</span> <span style="color: #e0e2ea;">-x</span> <span style="color: #e0e2ea;">mitch-preonic.json</span> <span style="color: #e0e2ea;">-x</span> <span style="color: #e0e2ea;">install</span> <span style="color: #e0e2ea;">-x</span> <span style="color: #e0e2ea;">rcup</span> <span style="color: #e0e2ea;">-v</span>
</div></code></pre>
<h2><a href="#room-for-improvement" aria-hidden="true" class="anchor" id="room-for-improvement"></a>Room for improvement</h2>
<p>My install script automates <em>most</em> things, but there are still some places optimize.</p>
<ul>
<li>Automate the manual steps with a "curl to bash" script that installs XCode and clones my dotfiles. e.g. <code>curl https://raw.githubusercontent.com/mhanberg/.dotfiles/master/bootstrap.sh | bash</code>.</li>
<li>Create a new ssh key.</li>
<li>Configure macOS specific settings like key repeat speed, dock size/behavior, etc.</li>
<li>Install my <code>zsh</code> plugins with <a href="https://github.com/zplug/zplug">zplug</a>.</li>
</ul> ]]></description>
    </item>
    <item>
       <title>Introducing Temple: An elegant HTML library for Elixir and Phoenix</title>
       <link>https://www.mitchellhanberg.com/introducing-temple-an-elegant-html-library-for-elixir-and-phoenix/</link>
       <pubDate>Fri, 12 Jul 2019 09:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/introducing-temple-an-elegant-html-library-for-elixir-and-phoenix/</guid>
       <description><![CDATA[ <img src="/images/temple.png" class="bg-transparent mx-auto my-16 [filter:invert(100%)]">
<p>Temple is a macro DSL for writing HTML markup in <a href="https://elixir-lang.org">Elixir</a> and a template engine for <a href="https://phoenixframework.org/">Phoenix</a>.</p>
<p>Conventional template languages like <a href="https://hexdocs.pm/eex/EEx.html">EEx</a> use a form of interpolation to embed a programming language into a markup language, which can result in some ugly code that can be difficult to write and debug.</p>
<p>Temple is written using <em>pure</em> elixir.</p>
<p>Let's take a look.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">use</span> <span style="color: #e0e2ea;">Temple</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">items <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;eggs&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;milk&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;bacon&quot;</span><span style="color: #e0e2ea;">]</span></span></span></span></span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #8cf8f7;">temple</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="6">  <span style="color: #8cf8f7;">h2</span> <span style="color: #b3f6c0;">&quot;todos&quot;</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8">  <span style="color: #8cf8f7;">ul</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;list&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="9">    <span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">item</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">items</span></span></span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="10">      <span style="color: #8cf8f7;">li</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;item&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="11">        <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;checkbox&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="12">          <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;bullet hidden&quot;</span>
</div><div class="line" data-line="13">        <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="14">
</div><div class="line" data-line="15">        <span style="color: #8cf8f7;">div</span> <span style="color: #e0e2ea;">item</span>
</div><div class="line" data-line="16">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="17">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="18">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="19">
</div><div class="line" data-line="20">  <span style="color: #8cf8f7;">script</span> <span style="color: #b3f6c0;">&quot;&quot;&quot;</span>
</div><div class="line" data-line="21"><span style="color: #b3f6c0;">  function toggleCheck(&lbrace;currentTarget&rbrace;) &lbrace;</span>
</div><div class="line" data-line="22"><span style="color: #b3f6c0;">    currentTarget.children[0].children[0].classList.toggle(&quot;hidden&quot;);</span>
</div><div class="line" data-line="23"><span style="color: #b3f6c0;">  &rbrace;</span>
</div><div class="line" data-line="24"><span style="color: #b3f6c0;"></span>
</div><div class="line" data-line="25"><span style="color: #b3f6c0;">  let items = document.querySelectorAll(&quot;li&quot;);</span>
</div><div class="line" data-line="26"><span style="color: #b3f6c0;"></span>
</div><div class="line" data-line="27"><span style="color: #b3f6c0;">  Array.from(items).forEach(checkbox =&gt;</span>
</div><div class="line" data-line="28"><span style="color: #b3f6c0;">    checkbox.addEventListener(&quot;click&quot;, toggleCheck)</span>
</div><div class="line" data-line="29"><span style="color: #b3f6c0;">  );</span>
</div><div class="line" data-line="30"><span style="color: #b3f6c0;">  &quot;&quot;&quot;</span>
</div><div class="line" data-line="31"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<h2><a href="#phoenix-template-engine" aria-hidden="true" class="anchor" id="phoenix-template-engine"></a>Phoenix Template Engine</h2>
<p>By implementing the <code>Phoenix.Template.Engine</code> behaviour, Temple becomes a full fledged template engine.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;"># config/config.exs</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #8cf8f7;">config</span> <span style="color: #8cf8f7;">:phoenix</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:template_engines</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">exs: </span><span style="color: #e0e2ea;">Temple.Engine</span>
</div></code></pre>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;"># lib/blog_web.ex</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">view</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">quote</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">    <span style="color: #9b9ea4;"># ...</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7">    <span style="color: #e0e2ea; font-weight: bold;">use</span> <span style="color: #e0e2ea;">Temple</span>
</div><div class="line" data-line="8">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;"># lib/blog_web/templates/post/show.html.exs</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #8cf8f7;">h2</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">post</span></span></span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">title</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #8cf8f7;">div</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="6">  <span style="color: #8cf8f7;">text</span> <span style="color: #b3f6c0;">&quot;By <span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">join</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">post</span></span></span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">authors</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;, &quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #8cf8f7;">&rbrace;</span>&quot;</span>
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9"><span style="color: #8cf8f7;">article</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="10">  <span style="color: #8cf8f7;">text</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">post</span></span></span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">body</span>
</div><div class="line" data-line="11"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="12">
</div><div class="line" data-line="13"><span style="color: #8cf8f7;">aside</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="14">  <span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">c</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">post</span></span></span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">comments</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="15">    <span style="color: #8cf8f7;">div</span> <span style="color: #e0e2ea;">c</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span>
</div><div class="line" data-line="16">    <span style="color: #8cf8f7;">div</span> <span style="color: #e0e2ea;">c</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">body</span>
</div><div class="line" data-line="17">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="18"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<h2><a href="#phoenixhtml" aria-hidden="true" class="anchor" id="phoenixhtml"></a>Phoenix.HTML</h2>
<p>All the Phoenix form and link helpers have been wrapped to be compatible with Temple.</p>
<p>The semantics and naming of some helpers have been change to work as macros and avoid name conflicts with standard HTML5 tags.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;"># takes a block and has access to the variable `form`</span>
</div><div class="line" data-line="2"><span style="color: #8cf8f7;">form_for</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">post</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">Routes</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">post_path</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">conn</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:create</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #9b9ea4;"># prefixed with `phx_` to avoid a conflict with the &lt;label&gt; tag</span>
</div><div class="line" data-line="5">  <span style="color: #8cf8f7;">phx_label</span> <span style="color: #e0e2ea;">form</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:title</span>
</div><div class="line" data-line="6">  <span style="color: #8cf8f7;">text_input</span> <span style="color: #e0e2ea;">form</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:title</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8">  <span style="color: #8cf8f7;">phx_label</span> <span style="color: #e0e2ea;">form</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:authors</span>
</div><div class="line" data-line="9">  <span style="color: #8cf8f7;">multiple_select</span> <span style="color: #e0e2ea;">form</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:authors</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">users</span></span></span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11">  <span style="color: #8cf8f7;">phx_label</span> <span style="color: #e0e2ea;">form</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:body</span>
</div><div class="line" data-line="12">  <span style="color: #8cf8f7;">text_area</span> <span style="color: #e0e2ea;">form</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:body</span>
</div><div class="line" data-line="13"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<h2><a href="#components" aria-hidden="true" class="anchor" id="components"></a>Components</h2>
<p>Temple offers <a href="https://reactjs.org">React-ish</a> style API for extracting reusable components.</p>
<p><code>defcomponent</code> will define macros for you to use in your markup without them looking any different from normal tags.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;"># layout_view.ex</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">MyAppWeb.LayoutView</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">use</span> <span style="color: #e0e2ea;">MyAppWeb</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:view</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6">  <span style="color: #8cf8f7;">defcomponent</span> <span style="color: #8cf8f7;">:nav_item</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="7">    <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">id: </span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">id</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;flex flex-col&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="8">      <span style="color: #8cf8f7;">div</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">name</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;margin-bottom-2&quot;</span>
</div><div class="line" data-line="9">      <span style="color: #8cf8f7;">div</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">description</span></span></span>
</div><div class="line" data-line="10">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="11">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="12"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="13">
</div><div class="line" data-line="14"><span style="color: #9b9ea4;"># app.html.exs</span>
</div><div class="line" data-line="15">
</div><div class="line" data-line="16"><span style="color: #8cf8f7;">html</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="17">  <span style="color: #8cf8f7;">head</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="18">    <span style="color: #9b9ea4;"># stuff</span>
</div><div class="line" data-line="19">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="20">
</div><div class="line" data-line="21">  <span style="color: #8cf8f7;">body</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="22">    <span style="color: #8cf8f7;">header</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="23">      <span style="color: #8cf8f7;">nav</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="24">        <span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">item</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">nav_items</span></span></span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="25">          <span style="color: #8cf8f7;">nav_item</span> <span style="color: #8cf8f7;">id: </span><span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">key</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="26">                   <span style="color: #8cf8f7;">name: </span><span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="27">                   <span style="color: #8cf8f7;">description: </span><span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">description</span>
</div><div class="line" data-line="28">        <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="29">      <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="30">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="31">
</div><div class="line" data-line="32">    <span style="color: #8cf8f7;">main</span> <span style="color: #8cf8f7;">role: </span><span style="color: #b3f6c0;">&quot;main&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;container&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="33">      <span style="color: #8cf8f7;">p</span> <span style="color: #8cf8f7;">get_flash</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">conn</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:info</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;alert alert-info&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">role: </span><span style="color: #b3f6c0;">&quot;alert&quot;</span>
</div><div class="line" data-line="34">      <span style="color: #8cf8f7;">p</span> <span style="color: #8cf8f7;">get_flash</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">conn</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:error</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;alert alert-danger&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">role: </span><span style="color: #b3f6c0;">&quot;alert&quot;</span>
</div><div class="line" data-line="35">
</div><div class="line" data-line="36">      <span style="color: #8cf8f7;">partial</span> <span style="color: #8cf8f7;">render</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">view_module</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">view_template</span></span></span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">assigns</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="37">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="38">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="39"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>Components can also take children if passed a block and are accessed via the <code>@children</code> variable.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">defcomponent</span> <span style="color: #8cf8f7;">:takes_children</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class: </span><span style="color: #b3f6c0;">&quot;some-wrapping-class&quot;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="3">    <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">children</span></span></span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #8cf8f7;">takes_children</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="8">  <span style="color: #8cf8f7;">span</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="9">    <span style="color: #8cf8f7;">text</span> <span style="color: #b3f6c0;">&quot;child one&quot;</span>
</div><div class="line" data-line="10">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="11">  <span style="color: #8cf8f7;">span</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="12">    <span style="color: #8cf8f7;">text</span> <span style="color: #b3f6c0;">&quot;child two&quot;</span>
</div><div class="line" data-line="13">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="14">  <span style="color: #8cf8f7;">span</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="15">    <span style="color: #8cf8f7;">text</span> <span style="color: #b3f6c0;">&quot;child three&quot;</span>
</div><div class="line" data-line="16">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="17"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="18">
</div><div class="line" data-line="19"><span style="color: #9b9ea4;"># &lt;div class=&quot;some-wrapping-class&quot;&gt;</span>
</div><div class="line" data-line="20"><span style="color: #9b9ea4;">#   &lt;span&gt;child one&lt;/span&gt;</span>
</div><div class="line" data-line="21"><span style="color: #9b9ea4;">#   &lt;span&gt;child two&lt;/span&gt;</span>
</div><div class="line" data-line="22"><span style="color: #9b9ea4;">#   &lt;span&gt;child three&lt;/span&gt;</span>
</div><div class="line" data-line="23"><span style="color: #9b9ea4;"># &lt;/div&gt;</span>
</div></code></pre>
<h2><a href="#wrapping-up" aria-hidden="true" class="anchor" id="wrapping-up"></a>Wrapping up</h2>
<p>If you're interested in using Temple, you can install it from <a href="https://hex.pm/packages/temple">Hex</a> and check it out on <a href="https://github.com/mhanberg/temple">GitHub</a>.</p>
<p>Let me know what you think!</p> ]]></description>
    </item>
    <item>
       <title>Implementing link following with OAuth</title>
       <link>https://www.mitchellhanberg.com/implementing-link-following-with-oauth/</link>
       <pubDate>Sun, 07 Apr 2019 09:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/implementing-link-following-with-oauth/</guid>
       <description><![CDATA[ <p><strong>Link following</strong> is the process for dynamically redirecting a user after successful authentication.</p>
<p>Disclaimer: I'm not sure what this is actually called, so I made up "Link following." If you know the actual name, <a href="https://twitter.com/mitchhanberg">let me know!</a></p>
<p>If you are new to using OAuth to handle authentication, implementing link following won't be as intuitive as it would be if you were hand rolling your own.</p>
<p>The trouble comes with remembering the link that the user clicked. If you were to write your own auth, this would all be handled within your own application and it would be easy to maintain that state. With OAuth, authentication is actually split between your application and the OAuth service that you don't control.</p>
<p>So how are you supposed to remember where the user wanted to go after the OAuth service authenticates the user?</p>
<p>The OAuth protocol specifies a <a href="https://auth0.com/docs/protocols/oauth2/oauth-state">state</a> parameter that can be sent along with the authentication request and is returned with the response.</p>
<h2><a href="#implementation" aria-hidden="true" class="anchor" id="implementation"></a>Implementation</h2>
<p>I recently implemented link following with the <a href="https://elixir-lang.org/">Elixir</a> libraries <a href="https://phoenixframework.org/">Phoenix</a> and <a href="https://github.com/ueberauth/ueberauth">Ueberauth</a>, but these principles apply to any language or library.</p>
<p>When the request comes in, we need to determine if the user is logged in. If they aren't we will need to redirect them to the log in page and take note of the original request path. Let's define a plug to do this for us.</p>
<p>(The code snippets here are stripped down for brevity)</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">MyAppWeb.LoggedIn</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">call</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_opts</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="3">    <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #8cf8f7;">current_user</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="4">      <span style="color: #8cf8f7;">nil</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="5">        <span style="color: #e0e2ea;">conn</span>
</div><div class="line" data-line="6">        <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">Phoenix.Controller</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">redirect</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">to:</span>
</div><div class="line" data-line="7"><span style="color: #8cf8f7;"></span>          <span style="color: #e0e2ea;">Routes</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">some_controller_path</span><span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="8">            <span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="9">            <span style="color: #8cf8f7;">:login</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="10">            <span style="color: #8cf8f7;">path: </span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">request_path</span>
</div><div class="line" data-line="11">          <span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="12">        <span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="13">        <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #8cf8f7;">halt</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="14">
</div><div class="line" data-line="15">      <span style="color: #9b9ea4;">_</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="16">        <span style="color: #e0e2ea;">conn</span>
</div><div class="line" data-line="17">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="18">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="19"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>Now that we are sending the original request path along with the redirect to the login page, we'll want to use that information to form our OAuth link.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">link</span><span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">to: </span><span style="color: #e0e2ea;">Routes</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">auth_path</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">:request</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">    <span style="color: #b3f6c0;">&quot;oauth service&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5">    <span style="color: #8cf8f7;">state: </span><span style="color: #e0e2ea;">path</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="7"><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<p>If the user is able to authenticate with the OAuth service, we'll be able to take advantage of the state parameter that is sent back with the response in our controller action.</p>
<p>The <code>User.find_or_create/1</code> function attempts to find an existing user and will create one if it can't, returning the user.</p>
<p><code>handle_find_for_create/2</code> will handle the response from the previous function, such as putting the user in the session and setting the flash message, returning the conn.</p>
<p><code>redirect/2</code> will send the user to their original destination based on the state parameter returned by the OAuth service.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">callback</span><span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="2">    <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">assigns: </span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">ueberauth_auth: </span><span style="color: #e0e2ea;">auth</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #b3f6c0;">&quot;state&quot;</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">path</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">  <span style="color: #e0e2ea;">conn</span> <span style="color: #e0e2ea;">=</span>
</div><div class="line" data-line="6">    <span style="color: #e0e2ea;">auth</span>
</div><div class="line" data-line="7">    <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #e0e2ea;">User</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">find_or_create</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="8">    <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #8cf8f7;">handle_find_or_create</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="9">
</div><div class="line" data-line="10">  <span style="color: #8cf8f7;">redirect</span> <span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">to: </span><span style="color: #e0e2ea;">path</span>
</div><div class="line" data-line="11"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<h2><a href="#wrapping-up" aria-hidden="true" class="anchor" id="wrapping-up"></a>Wrapping up</h2>
<p>The OAuth protocol takes a state parameter that allows you to maintain a piece of data during the authentication handshake, we can use this to remember what link the user was attempting to access, and redirect them after a successful handshake.</p> ]]></description>
    </item>
    <item>
       <title>Tools I use for mobile web development</title>
       <link>https://www.mitchellhanberg.com/tools-i-use-for-mobile-web-development/</link>
       <pubDate>Thu, 04 Apr 2019 09:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/tools-i-use-for-mobile-web-development/</guid>
       <description><![CDATA[ <p>It's never been easier to write web applications for mobile and I want to share some of the tools I use everyday that help me do so.</p>
<h2><a href="#ngrok" aria-hidden="true" class="anchor" id="ngrok"></a>ngrok</h2>
<p><a href="https://ngrok.com/">ngrok</a> is a tool used to create encrypted web tunnels to your computer in order to expose a local development server to the internet.</p>
<p>While modern web browsers have "mobile mode" dev tools, they don't perform as well as actually using the app on your phone.</p>
<p>My practice is to open the app on my phone and computer side by side, allowing me to make sure that the design looks great at all screen sizes.</p>
<h2><a href="#live-reload" aria-hidden="true" class="anchor" id="live-reload"></a>Live reload</h2>
<p>Live reloading typically involves a file watcher that triggers a browser refresh when files change on disk.  Manually reloading the page is a pain, especially when you have your app open in multiple browsers and devices.</p>
<p>Luckily, many web frameworks come with built-in live reloading functionality.</p>
<p>The tools that I use often that provide this are <a href="https://jekyllrb.com/">Jekyll</a> <code>jekyll serve --livereload</code> and the Elixir web framework <a href="https://phoenixframework.org/">Phoenix</a> <code>iex -S mix phx.server</code> (it works without any extra options).</p>
<h2><a href="#safari-mobile-web-inspector" aria-hidden="true" class="anchor" id="safari-mobile-web-inspector"></a>Safari mobile web inspector</h2>
<p>I use the Mobile Safari dev tools to debug JavaScript and design in the browser when I'm running to problems with my applications on mobile.</p>
<p>To use the Web Inspector on your iPhone, toggle the following option under Settings > Safari > Advanced and following the instructions.</p>
<p><img src="/images/safari-web-inspector.jpg" alt="Picture of the web inspector option for the Safari web browser on iOS" /></p>
<h2><a href="#tailwind-css" aria-hidden="true" class="anchor" id="tailwind-css"></a>Tailwind CSS</h2>
<p>My favorite tool for making mobile friendly applications is the <a href="https://tailwindcss.com/docs/what-is-tailwind/">Tailwind CSS</a> utility-first CSS framework.</p>
<p>Tailwind generates responsive versions of all of it's CSS classes and provides custom <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule">at-rules</a> to help with writing your own.</p>
<p>Here are a couple examples of responsive design using Tailwind.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-html" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;">&lt;!-- progressively scale headings as the screen width increases --&gt;</span>
</div><div class="line" data-line="2"><span style="color: #8cf8f7;">&lt;</span>h2 <span style="color: #8cf8f7;">class</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">&quot;text-2xl md:text-3xl&quot;</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="3">  <span style="color: #e0e2ea; font-weight: bold;">Developing for Mobile Efficiently</span>
</div><div class="line" data-line="4"><span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">h2</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6"><span style="color: #9b9ea4;">&lt;!-- start as as a column and move to a row at medium size screens --&gt;</span>
</div><div class="line" data-line="7"><span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">&quot;flex flex-col md:flex-row&quot;</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="8">  <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">div</span><span style="color: #8cf8f7;">&gt;</span>1<span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">div</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="9">  <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">div</span><span style="color: #8cf8f7;">&gt;</span>2<span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">div</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="10">  <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">div</span><span style="color: #8cf8f7;">&gt;</span>3<span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">div</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="11"><span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">div</span><span style="color: #8cf8f7;">&gt;</span>
</div></code></pre>
<p>You can create your own responsive classes using the <code>@responsive</code> at-rule.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-css" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">@responsive</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">my-style</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="3">    <span style="color: #9b9ea4;">/* styles... */</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<h2><a href="#what-else-is-out-there" aria-hidden="true" class="anchor" id="what-else-is-out-there"></a>What else is out there?</h2>
<p>I'm always interested in finding new tools, so if you use something not mentioned here, please share it with me on Twitter! <a href="https://twitter.com/mitchhanberg">@mitchhanberg</a></p> ]]></description>
    </item>
    <item>
       <title>Experiment in the REPL</title>
       <link>https://www.mitchellhanberg.com/experiment-in-the-repl/</link>
       <pubDate>Tue, 19 Feb 2019 11:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/experiment-in-the-repl/</guid>
       <description><![CDATA[ <p>A technique I've picked up for learning new tools is <strong>experimenting with them in the REPL</strong>.</p>
<blockquote>
<p>A read–eval–print loop (REPL), is a simple, interactive computer programming environment that takes single user inputs, evaluates them, and returns the result to the user. <a href="https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop">*</a></p>
</blockquote>
<p>Elixir (<code>iex</code>), Ruby (<code>irb</code>), and Node.js (<code>node</code>) all have interactive shells that allow you to evaluate expressions easily and quickly.</p>
<p>This works great for small things like remembering how division works or grokking a tricky enumerable method.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">iex(1)&gt; 1 / 2
</div><div class="line" data-line="2">0.5
</div><div class="line" data-line="3">iex(2)&gt; div 1, 2
</div><div class="line" data-line="4">0
</div><div class="line" data-line="5">iex(3)&gt; Enum.reject([1, 2, 3], fn n -&gt; rem(n, 2) == 0 end)
</div><div class="line" data-line="6">[1, 3]
</div></code></pre>
<p>It's also a convenient way to try out new libraries.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">iex(4)&gt; Slack.Web.Chat.post_message(&quot;C68FV6MDH&quot;, nil, %&lbrace;attachments: [%&lbrace;color: &quot;good&quot;, text: &quot;Hello world!&quot;&rbrace;]&rbrace;)
</div><div class="line" data-line="2">** (ArgumentError) argument error
</div><div class="line" data-line="3">    :erlang.list_to_binary([%&lbrace;color: &quot;good&quot;, text: &quot;Hello world!&quot;&rbrace;])
</div><div class="line" data-line="4">    (hackney) /Users/mitchell/Development/my_app/deps/hackney/src/hackney_bstr.erl:36: :hackney_bstr.to_binary/1
</div><div class="line" data-line="5">    (hackney) /Users/mitchell/Development/my_app/deps/hackney/src/hackney_url.erl:300: :hackney_url.urlencode/2
</div><div class="line" data-line="6">    (hackney) /Users/mitchell/Development/my_app/deps/hackney/src/hackney_url.erl:360: :hackney_url.qs/3
</div><div class="line" data-line="7">    (hackney) /Users/mitchell/Development/my_app/deps/hackney/src/hackney_request.erl:310: :hackney_request.encode_form/1
</div><div class="line" data-line="8">    (hackney) /Users/mitchell/Development/my_app/deps/hackney/src/hackney_request.erl:318: :hackney_request.handle_body/4
</div><div class="line" data-line="9">    (hackney) /Users/mitchell/Development/my_app/deps/hackney/src/hackney_request.erl:81: :hackney_request.perform/2
</div><div class="line" data-line="10">    (hackney) /Users/mitchell/Development/my_app/deps/hackney/src/hackney.erl:373: :hackney.send_request/2
</div><div class="line" data-line="11">    (httpoison) lib/httpoison/base.ex:787: HTTPoison.Base.request/6
</div><div class="line" data-line="12">    (httpoison) lib/httpoison.ex:128: HTTPoison.request!/5
</div><div class="line" data-line="13">    (slack) lib/slack/web/web.ex:51: Slack.Web.Chat.post_message/3
</div><div class="line" data-line="14">
</div><div class="line" data-line="15"># Turns out we need to JSON encode the attachments list
</div><div class="line" data-line="16">iex(5)&gt;Slack.Web.Chat.post_message(&quot;C68FV6MDH&quot;, nil, %&lbrace;attachments: Jason.encode!([%&lbrace;color: &quot;good&quot;, text: &quot;Hello world!&quot;&rbrace;])&rbrace;)
</div><div class="line" data-line="17">%&lbrace;
</div><div class="line" data-line="18">  &quot;channel&quot; =&gt; &quot;C68FV6MDH&quot;,
</div><div class="line" data-line="19">  &quot;ok&quot; =&gt; true,
</div><div class="line" data-line="20">&rbrace; 
</div></code></pre>
<p>I find experimenting in the REPL to be useful when I need a <strong>short feedback loop</strong> that isn't tightly coupled to the full stack of my application.</p>
<h2><a href="#avoid-analysis-paralysis" aria-hidden="true" class="anchor" id="avoid-analysis-paralysis"></a>Avoid Analysis Paralysis</h2>
<blockquote>
<p>Analysis paralysis is when the fear of potential error outweighs the realistic expectation or potential value of success, and this imbalance results in suppressed decision-making in an unconscious effort to preserve existing options. <a href="https://en.wikipedia.org/wiki/Analysis_paralysis#Software_development">*</a></p>
</blockquote>
<p>Idle hands build nothing. I fell into this trap recently, wasting hours before I remembered the power of the REPL.</p>
<p>I knew I needed to use either a <a href="https://hexdocs.pm/elixir/Supervisor.html">Supervisor</a> or a <a href="https://hexdocs.pm/elixir/DynamicSupervisor.html">Dynamic Supervisor</a>, but I wasn't sure which one was right for the job.</p>
<p>Unclear on how to proceed, I spent a <strong>few hours</strong> reading documentation, searching for blog posts, and asking people for advice. Did I learn enough to make a decision? <em>No</em>.</p>
<p>Then I spent <strong>20 minutes</strong> experimenting with them in <code>iex</code> and I knew which one to use.</p> ]]></description>
    </item>
    <item>
       <title>Tips for Reading More</title>
       <link>https://www.mitchellhanberg.com/tips-for-reading-more/</link>
       <pubDate>Thu, 14 Feb 2019 13:30:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/tips-for-reading-more/</guid>
       <description><![CDATA[ <p>In 2017, I challenged myself to read 20 books and I was able to read 22 books.</p>
<p>In 2018, I raised the bar to <em>30</em> books, but I was only able to read 16.</p>
<p>In 2019, I'm still aiming for 30 books; how can I make this happen?</p>
<hr />
<p>So far in 2019 I've already read 7 books and have found good strategies for frequent and consistent reading.</p>
<p>I've started <strong>reading during boring activities that don't require my eyes</strong>. I read in bed when I'm struggling to fall asleep. I read in the morning when I'm riding my stationary bike. This is similar to the idea of listening to a podcast while driving to work.</p>
<p>I've started <strong>buying books in groups of three</strong>. Always having something in the queue decreases the number of times that I have to figure out what I'm going to read next.</p>
<p>I've started <strong>asking my friends for book recommendations</strong>. This decreases the risk of starting a bad book and relieves <a href="https://xkcd.com/1801/">decision paralysis</a>. I'll show the list of <a href="https://www.goodreads.com/review/list/69703261-mitchell?shelf=to-read">books I want to read</a> to a friend and ask if they recommend anything on the list, essentially cross referencing <em>my</em> interests with <em>their</em> recommendations.</p>
<p>I've started <strong>sharing what I'm reading</strong>. When I make my three-book purchase, I share a <a href="https://twitter.com/mitchhanberg/status/1083199091646046208?s=20">screenshot on Twitter</a>. This creates pseudo social pressure to finish and sharing it in the open helps solidify my <a href="https://jamesclear.com/identity-based-habits">identity</a> as a "reader."</p> ]]></description>
    </item>
    <item>
       <title>Creating Responsive Popovers with Popper.js</title>
       <link>https://www.mitchellhanberg.com/creating-responsive-popovers-with-popper.js/</link>
       <pubDate>Mon, 11 Feb 2019 09:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/creating-responsive-popovers-with-popper.js/</guid>
       <description><![CDATA[ <p>Making <em>simple</em> popovers is pretty easy.</p>
<p>Making popovers that position themselves based on the available screen real estate so they're <em>always visible</em> is not.</p>
<p>Luckily <a href="https://popper.js.org/">Popper.js</a> will do the math for us and is straight forward to implement given the proper instructions.</p>
<h2><a href="#javascript" aria-hidden="true" class="anchor" id="javascript"></a>JavaScript</h2>
<p>The JavaScript portion is simple. The <code>Popper</code> constructor takes a DOM node to attach the popover to and a DOM node that will be the body of the popover.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-javascript" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;">// index.js</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">const</span> <span style="color: #e0e2ea;">attachmentNode</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">document</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">querySelector</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;#attachment-point&quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="4"><span style="color: #e0e2ea; font-weight: bold;">const</span> <span style="color: #e0e2ea;">popoverNode</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">document</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">querySelector</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;#my-popover&quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6"><span style="color: #e0e2ea; font-weight: bold;">const</span> <span style="color: #e0e2ea;">myPopper</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea; font-weight: bold;">new</span> <span style="color: #8cf8f7;">Popper</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">attachmentNode</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">popoverNode</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div></code></pre>
<h2><a href="#html-and-css" aria-hidden="true" class="anchor" id="html-and-css"></a>HTML and CSS</h2>
<p>The HTML and CSS is simple unless you want an arrow tab on your popover.</p>
<p>The docs make it seem like the arrow comes for free, but you'll have to implement it yourself.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-html" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;">&lt;!-- index.html --&gt;</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">button</span> <span style="color: #8cf8f7;">id</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">&quot;attachment-point&quot;</span><span style="color: #8cf8f7;">&gt;</span> Open Sesame! <span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">button</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">class</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">&quot;popover&quot;</span> <span style="color: #8cf8f7;">id</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">&quot;my-popover&quot;</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="6">  <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">div</span> <span style="color: #8cf8f7;">x-arrow</span><span style="color: #8cf8f7;">&gt;</span><span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">div</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8">  <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">div</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="9">    <span style="color: #9b9ea4;">&lt;!-- your popover content here --&gt;</span>
</div><div class="line" data-line="10">  <span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">div</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="11"><span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">div</span><span style="color: #8cf8f7;">&gt;</span>
</div></code></pre>
<p>Popper.js will look for an element with the <code>x-arrow</code> attribute to use for the arrow tab.</p>
<p>In order for the arrow to be on the correct side of the popover, we need to style it as described below.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-css" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;">/* styles.css */</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">x-arrow</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="4">  <span style="color: #a6dbff;">position</span><span style="color: #e0e2ea;">:</span> absolute<span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">popover</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="8">  <span style="color: #a6dbff;">margin-top</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">10<span style="color: #b3f6c0;">px</span></span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="9">  <span style="color: #a6dbff;">margin-bottom</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">10<span style="color: #b3f6c0;">px</span></span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="10"><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="11">
</div><div class="line" data-line="12"><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">popover</span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">x-placement</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">&quot;bottom&quot;</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">x-arrow</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="13">  <span style="color: #a6dbff;">top</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">-10<span style="color: #b3f6c0;">px</span></span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="14">  <span style="color: #a6dbff;">border-bottom</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">10<span style="color: #b3f6c0;">px</span></span> solid white<span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="15">  <span style="color: #a6dbff;">border-right</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">10<span style="color: #b3f6c0;">px</span></span> solid transparent<span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="16">  <span style="color: #a6dbff;">border-left</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">10<span style="color: #b3f6c0;">px</span></span> solid transparent<span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="17"><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="18">
</div><div class="line" data-line="19"><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">popover</span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">x-placement</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">&quot;top&quot;</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">x-arrow</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="20">  <span style="color: #a6dbff;">bottom</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">-10<span style="color: #b3f6c0;">px</span></span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="21">  <span style="color: #a6dbff;">border-top</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">10<span style="color: #b3f6c0;">px</span></span> solid white<span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="22">  <span style="color: #a6dbff;">border-right</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">10<span style="color: #b3f6c0;">px</span></span> solid transparent<span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="23">  <span style="color: #a6dbff;">border-left</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">10<span style="color: #b3f6c0;">px</span></span> solid transparent<span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="24"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<p>The <code>x-placement</code> attribute added by Popper.js allows you to style the arrow based on the orientation of the popover.</p>
<p>The goofiness you see with the borders is a hack you can use to create a triangle using a <code>div</code>. I haven't tried it, but I imagine using an <code>svg</code> yields better results.</p>
<h2><a href="#wrapping-up" aria-hidden="true" class="anchor" id="wrapping-up"></a>Wrapping Up</h2>
<p>Popper.js takes care of the hard part so you can focus on building your application.</p>
<p>If you want the popover to show on click or hover, you'll have to add a little more JavaScript and CSS.</p>
<p>Hop into this Code Sandbox and give it a shot.</p>
<iframe src="https://codesandbox.io/embed/501wn1yvk?hidenavigation=1" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> ]]></description>
    </item>
    <item>
       <title>Conducting Good Retrospectives</title>
       <link>https://www.mitchellhanberg.com/post/2018/11/09/conducting-good-retrospectives/</link>
       <pubDate>Fri, 09 Nov 2018 21:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2018/11/09/conducting-good-retrospectives/</guid>
       <description><![CDATA[ <p>Every team experiences moments of success or failure, moments of working like a well oiled machine, and moments of not being able to stand the sound of each other's voices. But many teams let these moments pass by without taking time to ponder "why did these things happen"?</p>
<p>A structured way to make sure your team doesn't miss asking that question is to hold a <strong>retrospective</strong>.</p>
<h2><a href="#what-is-a-retrospective" aria-hidden="true" class="anchor" id="what-is-a-retrospective"></a>What is a retrospective?</h2>
<p>A <a href="https://www.scrum.org/resources/what-is-a-sprint-retrospective">retrospective</a> (or "retro") is a recurring meeting used by teams to look back at the last sprint, week, or any major event in order to figure out what went well and what went poorly.</p>
<p>The realm of topics is wide ranging: from mishandled social interactions to achieving a major milestone. These meetings are not intended to make you feel good or bad about yourselves, they're designed to create actionable items for your team.</p>
<p>Setting aside dedicated time for these matters assures that they will get some attention and helps keep these conversations from spilling into normal work time.</p>
<p>Along with <strong>learning from your successes and failures</strong>, one of the goals of a retrospective to <strong>build trust among your team</strong>. Teams perform better when they operate in an environment of <a href="https://hbr.org/2017/08/high-performing-teams-need-psychological-safety-heres-how-to-create-it">psychological safety</a>.</p>
<h2><a href="#how-to-conduct-a-retrospective" aria-hidden="true" class="anchor" id="how-to-conduct-a-retrospective"></a>How to conduct a retrospective</h2>
<p>Anyone can host a retrospective, but hosting a <em>good</em> retrospective takes finesse. There are five key aspects that are paramount to a good retrospective: Facilitation, Conversational Turn Taking, Format/Activity, Action Items, and Trust.</p>
<h3><a href="#facilitation" aria-hidden="true" class="anchor" id="facilitation"></a>Facilitation</h3>
<p>Someone should volunteer to facilitate the meeting and lead the chosen activity. Depending on the activity, this can include taking notes, keeping a conversation timer, or tracking whose turn it is to speak.</p>
<p>A facilitator <strong>isolates the responsibility of running the meeting to one person</strong> and allows everyone else to concentrate on participating.</p>
<h3><a href="#conversational-turn-taking" aria-hidden="true" class="anchor" id="conversational-turn-taking"></a>Conversational Turn Taking</h3>
<p>It's important that everyone is heard. Your team should decide on a signal that makes it clear when you have something to say. My team has opted to use a finger raise to signal when you want to say something. Once someone is done speaking, motion to the next person in line to speak.</p>
<p>When someone has a tangential point to contribute, they can delay their turn to speak to allow the current thread of discussion to finish.</p>
<p>The primary motivation for the technique to is <strong>avoid interruptions</strong> and <strong>talking over each other</strong>.</p>
<h3><a href="#formatactivity" aria-hidden="true" class="anchor" id="formatactivity"></a>Format/Activity</h3>
<p>Free form conversation is not conducive to reaching conclusions, so pick a meeting format that fits well with your team. At the beginning of the meeting, the facilitator will explain the format and confirm that everyone is okay with that decision. Different formats optimize for different aspects of your team: project performance, interpersonal communication, or general frustration.</p>
<p>Picking a format gives the meeting <strong>structure</strong> and an <strong>objective</strong>, allowing the team to work together towards a known and shared goal.</p>
<h3><a href="#action-items" aria-hidden="true" class="anchor" id="action-items"></a>Action Items</h3>
<p>A necessary output of the meeting is a list of action items. An action item is a task that requires further action in order to accomplish it and is assigned to a specific person. You've probably experienced the situation where everyone agrees something should get done, but no one ever does it.</p>
<p>Sometimes you run into an action item that isn't a specific task, but possibly a general change in behavior that is meant to be addressed by the whole team. In this case, the action item would be shepherding the adoption of this new behavior for a couple weeks.</p>
<p>Leaving the meeting with a list of action items ensures that the tasks you spent an hour discussing <strong>actually get done</strong>.</p>
<h3><a href="#trust" aria-hidden="true" class="anchor" id="trust"></a>Trust</h3>
<p>Building trust will take time, but it is the keystone to having productive retrospectives and becoming a great team.</p>
<p>People thrive when working in an environment where everyone has mutual respect and the freedom to speak their mind and take risks. Google conducted a study, <a href="https://www.nytimes.com/2016/02/28/magazine/what-google-learned-from-its-quest-to-build-the-perfect-team.html">Project Aristotle</a>, on hundreds of teams and determined that <strong>Psychological Safety</strong> is the most <a href="https://rework.withgoogle.com/blog/five-keys-to-a-successful-google-team/">important dynamic of a successful team</a>.</p>
<p>The leader of the study, Julia Rozovksy, said:</p>
<blockquote>
<p>Individuals on teams with higher psychological safety are less likely to leave Google, they’re more likely to harness the power of diverse ideas from their teammates, they bring in more revenue, and they’re rated as effective twice as often by executives.</p>
</blockquote>
<h2><a href="#case-study" aria-hidden="true" class="anchor" id="case-study"></a>Case Study</h2>
<p>For the last year, my team has had a weekly retro, alternating the format each time. We operate in two week sprints, so in the middle of the sprint we concentrate on interpersonal issues or other general frustrations and at the end of the sprint we focus on project-specific issues.</p>
<h3><a href="#mid-sprint-retrospective" aria-hidden="true" class="anchor" id="mid-sprint-retrospective"></a>Mid-Sprint Retrospective</h3>
<p>During the Mid-Sprint Retrospective, we engage in the <a href="http://leancoffee.org">Lean Coffee</a> activity. The chosen facilitator will explain the activity and pass out the required supplies.</p>
<p>The basics of the Lean Coffee activity are:</p>
<ol>
<li>Generate discussion topics
<ul>
<li>Limit to 5 minutes</li>
</ul>
</li>
<li>Vote on topics</li>
<li>Discuss topics
<ul>
<li>Limit to 5 minutes, then vote on continuing with a 2 minute extension, or else move on to next topic</li>
<li>Facilitator will take notes and track action items</li>
</ul>
</li>
</ol>
<p>Any topics that aren't discussed are carried over to the next meeting if anyone still has interest.</p>
<h3><a href="#sprint-retrospective" aria-hidden="true" class="anchor" id="sprint-retrospective"></a>Sprint Retrospective</h3>
<p>During the Sprint Retrospective we use a variant of the <a href="http://www.funretrospectives.com/lessons-learned-quadrants-planning-vs-success/">Four Quadrant</a> activity. As with the Mid-Sprint retro, the chosen facilitator will explain the activity and pass out the required supplies.</p>
<p>The basics of our version of the Four Quadrant activity are:</p>
<ol>
<li>Enumerate events that happened during the last two weeks on sticky notes.
<ul>
<li>Limit to 5 minutes</li>
</ul>
</li>
<li>The facilitator will draw a horizontal axis on the whiteboard, with the left side indicating <em>bad</em> and the right side <em>good</em>.</li>
<li>Each person will give a brief description of their stickies and place them on the axis where they think they belong.</li>
<li>The facilitator then draws a vertical axis, with the top indicating the event was <em>in the team's control</em> and the bottom that it was <em>not in the team's control</em>.</li>
<li>The facilitator quickly pulls each sticky to the appropriate place on the vertical axis. This is usually obvious, but the rest of the team will signal where they think it should go in case of disagreement.</li>
</ol>
<p>At this point, the board should resemble something like this:</p>
<p><img src="https://res.cloudinary.com/mhanberg/image/upload/v1541686229/four-quadrants.png" alt="Diagram of a completed Four Quadrants diagram" /></p>
<p>Starting in the top right corner and moving counter-clockwise, the team discusses the events in an attempt to learn. If your team is prone to drawing out discussions, consider using a timer.</p>
<p><strong>Keep doing</strong> - good and in your control: give yourselves a quick pat on the back to celebrate good work.</p>
<p><strong>Improve</strong> - bad and in your control: these are events that your team has the power to avoid in the future. The facilitator will lead discussion on the topics, driving toward specific action items.</p>
<p><strong>Escalate</strong> - bad and out of your control: these events can only be addressed by someone external to your team. Now is the time to figure out with who to escalate the issue, or assign someone the action of figuring that out later.</p>
<p><strong>Kudos</strong> - good and out of your control: these are positive events that were caused by someone external to your team. Here the team will assign an action to someone to reach out and express the team's gratitude.</p>
<h2><a href="#conclusion" aria-hidden="true" class="anchor" id="conclusion"></a>Conclusion</h2>
<p>Done right, retrospectives can be a boon for successful and struggling teams alike.</p>
<p>The goal is to <strong>build a culture of trust</strong> among your team, where everyone feels safe to express themselves without the fear of being punished for making a mistake or speaking their mind. If there are barriers to bringing up problems on your team, you can never expect to be able to work through them and improve.</p>
<p>If your team has never participated in a retrospective, <strong>my challenge to you is to bring the idea to your team and get it on the calendar!</strong></p> ]]></description>
    </item>
    <item>
       <title>Reducers: Exploring State Management in React (Part 2)</title>
       <link>https://www.mitchellhanberg.com/post/2018/10/24/reducers-exploring-state-management-in-react/</link>
       <pubDate>Tue, 23 Oct 2018 23:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2018/10/24/reducers-exploring-state-management-in-react/</guid>
       <description><![CDATA[ <p>In a previous <a href="https://www.mitchellhanberg.com/post/2018/07/25/exploring-state-management-in-react-container-components/">post</a>, we demonstrated how the Container/Presenter pattern is a solid approach to managing your React state. This time we are going to look into using Reducer functions as the method to managing change in state of your components.</p>
<h2><a href="#reduce" aria-hidden="true" class="anchor" id="reduce"></a>Reduce</h2>
<p>Reduce (also known as a <a href="https://en.wikipedia.org/wiki/Fold_%28higher-order_function%29">fold</a>) is a functional programming concept that deals with the transformation of data structures using recursion and higher order functions. If you have used either the <code>Array.prototype.reduce</code> or <code>Array.prototype.map</code> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array">functions</a>, you already have experience with this technique.</p>
<h2><a href="#the-approach" aria-hidden="true" class="anchor" id="the-approach"></a>The Approach</h2>
<p>The general approach is to have a <code>reduce</code> (can be named whatever you'd like) function that understands how to respond to certain messages and will output the transformed state. We'll normally call this <code>reduce</code> function from a <code>dispatch</code> function.</p>
<p>This is essentially the same pattern you use with <a href="https://redux.js.org">Redux</a>, but we don't need to install any packages to use it.</p>
<p>Let's examine the code snippet below.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-javascript" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">function</span> <span style="color: #8cf8f7;">reduce</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">prevState</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">message</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea; font-weight: bold;">switch</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">message</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">type</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="3">    <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #b3f6c0;">&quot;ADD&quot;</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="4">      <span style="color: #e0e2ea; font-weight: bold;">return</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="5">        <span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">...</span><span style="color: #e0e2ea;">prevState</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">prevState</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">newItem</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="6">        <span style="color: #e0e2ea;">newItem</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">&quot;&quot;</span>
</div><div class="line" data-line="7">      <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="8">    <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #b3f6c0;">&quot;REMOVE&quot;</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="9">      <span style="color: #e0e2ea; font-weight: bold;">return</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="10">        <span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">prevState</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">filter</span><span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="11">          <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">i</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">idx</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">prevItems</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">idx</span> <span style="color: #e0e2ea;">!==</span> <span style="color: #e0e2ea;">prevItems</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">indexOf</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">message</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="12">        <span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="13">      <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="14">    <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #b3f6c0;">&quot;CHANGE&quot;</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="15">      <span style="color: #e0e2ea; font-weight: bold;">return</span> <span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">newItem</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">message</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">item</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="16">    <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #b3f6c0;">&quot;DISCOUNTS&quot;</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="17">      <span style="color: #e0e2ea; font-weight: bold;">return</span> <span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">discounts</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">message</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">discounts</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="18">    <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #b3f6c0;">&quot;INITIAL&quot;</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="19">      <span style="color: #e0e2ea; font-weight: bold;">return</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="20">        <span style="color: #e0e2ea;">newItem</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">&quot;&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="21">        <span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="22">        <span style="color: #e0e2ea;">discounts</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="23">      <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="24">    <span style="color: #e0e2ea; font-weight: bold;">default</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="25">      <span style="color: #e0e2ea; font-weight: bold;">throw</span> <span style="color: #e0e2ea; font-weight: bold;">new</span> <span style="color: #8cf8f7;">Error</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;Unknown message type received&quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="26">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="27"><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="28">
</div><div class="line" data-line="29"><span style="color: #e0e2ea; font-weight: bold;">class</span> <span style="color: #e0e2ea;">Cart</span> <span style="color: #e0e2ea; font-weight: bold;">extends</span> <span style="color: #e0e2ea;">React</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">Component</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="30">  <span style="color: #e0e2ea;">state</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">reduce</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">undefined</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">type</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">&quot;INITIAL&quot;</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="31">
</div><div class="line" data-line="32">  <span style="color: #8cf8f7;">componentDidUpdate</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">prevProps</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">prevState</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="33">    <span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">prevState</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">length</span> <span style="color: #e0e2ea;">!==</span> <span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">length</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="34">      <span style="color: #e0e2ea; font-weight: bold;">const</span> <span style="color: #e0e2ea;">itemsQuery</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">join</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;,&quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="35">
</div><div class="line" data-line="36">      <span style="color: #8cf8f7;">ajax</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">`https://discountdb.com/?items=<span style="color: #8cf8f7;">$&lbrace;</span><span style="color: #e0e2ea;">itemsQuery</span><span style="color: #8cf8f7;">&rbrace;</span>`</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="37">        <span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">then</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">discounts</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">dispatch</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="38">          <span style="color: #e0e2ea;">type</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">&quot;DISCOUNTS&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">discounts</span> 
</div><div class="line" data-line="39">        <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="40">    <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="41">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="42">
</div><div class="line" data-line="43">  <span style="color: #e0e2ea;">dispatch</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">action</span> <span style="color: #e0e2ea;">=&gt;</span> 
</div><div class="line" data-line="44">    <span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">setState</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">prevState</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">reduce</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">prevState</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">action</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="45">
</div><div class="line" data-line="46">  <span style="color: #e0e2ea;">onNewItemChange</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">event</span> <span style="color: #e0e2ea;">=&gt;</span>
</div><div class="line" data-line="47">    <span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">dispatch</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">type</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">&quot;CHANGE&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">event</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">target</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">value</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="48">
</div><div class="line" data-line="49">  <span style="color: #e0e2ea;">addItem</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">dispatch</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">type</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">&quot;ADD&quot;</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="50">
</div><div class="line" data-line="51">  <span style="color: #e0e2ea;">removeItem</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">item</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">dispatch</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">type</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">&quot;REMOVE&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">item</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="52">
</div><div class="line" data-line="53">  <span style="color: #8cf8f7;">render</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="54">    <span style="color: #e0e2ea; font-weight: bold;">return</span> <span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="55">      <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">ul</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="56">        <span style="color: #8cf8f7;">&lt;</span>h2<span style="color: #8cf8f7;">&gt;</span><span style="color: #e0e2ea; font-weight: bold;">Cart</span><span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">h2</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="57">        <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">input</span>
</div><div class="line" data-line="58">          <span style="color: #8cf8f7;">type</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">&quot;text&quot;</span>
</div><div class="line" data-line="59">          <span style="color: #8cf8f7;">onChange</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">onNewItemChange</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="60">          <span style="color: #8cf8f7;">value</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">newItem</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="61">        <span style="color: #8cf8f7;">/&gt;</span>
</div><div class="line" data-line="62">        <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">button</span> <span style="color: #8cf8f7;">onClick</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">addItem</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #8cf8f7;">&gt;</span>Add item<span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">button</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="63">
</div><div class="line" data-line="64">        <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">idx</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="65">          <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">li</span> <span style="color: #8cf8f7;">key</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">idx</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="66">            <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">button</span> <span style="color: #8cf8f7;">onClick</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">removeItem</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #8cf8f7;">&gt;</span>Remove<span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">button</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="67">            <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">&rbrace;</span>, <span style="color: #8cf8f7;">&amp;ensp;</span>
</div><div class="line" data-line="68">            <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">discounts</span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">idx</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span>% off!
</div><div class="line" data-line="69">          <span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">li</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="70">        <span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="71">      <span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">ul</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="72">    <span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="73">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="74"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<p><a href="https://codesandbox.io/s/zrww3wp57m"><img src="https://codesandbox.io/static/img/play-codesandbox.svg" alt="Edit zrww3wp57m" /></a></p>
<p>Here our <code>reduce</code> function resolves to a switch statement which delegates according to certain messages. We call this function in two places: directly in our <code>state</code> initializer to bootstrap our component and in our <code>dispatch</code> function to allow our event handlers to dynamically pass messages.</p>
<p>Unlike the function you pass to <code>Array.prototype.reduce</code>, our reduce only returns the changes to state and not the entire new state. This is because we only pass changes to <code>this.setState</code>.</p>
<p><em>Notice that the only place we call <code>this.setState</code> directly is in the <code>dispatch</code> function.</em></p>
<h2><a href="#benefits" aria-hidden="true" class="anchor" id="benefits"></a>Benefits</h2>
<p>What we have done is extract and enumerate the various transformations that can happen to our component state.</p>
<p>Isolating our state transformations this way can be beneficial when it comes to unit testing; our reducer is just plain JavaScript (no React). We fully extracted all state transformations into the reducer, but you are free to only pull out the ones that can benefit from the indirection.</p>
<p>I have added this technique to a React codebase that was written with no state management patterns in mind, and I found that it really simplified and focused each component.</p>
<p>By creating the <code>dispatch</code> function, we are able to pass only one function as a prop to child components if they need to manipulate their parent's state. I found that this drastically reduces <a href="https://blog.kentcdodds.com/prop-drilling-bb62e02cb691">prop drilling</a>.</p>
<h2><a href="#drawbacks" aria-hidden="true" class="anchor" id="drawbacks"></a>Drawbacks</h2>
<p>While this allows you to pass fewer callbacks to your child components, you will still need to thread it through your component tree if you want to modify top-level state from a leaf-node.</p>
<p>If you're implementing this pattern and think to yourself (like how I felt writing the above contrived example), "Why am I even doing this?" your component(s) might not be complex enough to warrant this.</p>
<h2><a href="#wrapping-up" aria-hidden="true" class="anchor" id="wrapping-up"></a>Wrapping Up</h2>
<p>This isn't an original concept; <a href="https://twitter.com/dan_abramov">Dan Abramov</a> has discussed this before and his <a href="https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367">article</a> is also worth reading. This post is mostly an exercise in exploring different ways to organize and transform React component state.</p>
<p>Next time you are working with a React component, try to think of new ways you can work with state and let me know what you come up with on Twitter: <a href="https://twitter.com/mitchhanberg">@mitchhanberg</a>.</p> ]]></description>
    </item>
    <item>
       <title>How to use Elixir LS with Vim</title>
       <link>https://www.mitchellhanberg.com/post/2018/10/18/how-to-use-elixir-ls-with-vim/</link>
       <pubDate>Thu, 18 Oct 2018 08:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2018/10/18/how-to-use-elixir-ls-with-vim/</guid>
       <description><![CDATA[ <h2><a href="#what-is-elixir-ls" aria-hidden="true" class="anchor" id="what-is-elixir-ls"></a>What is Elixir LS?</h2>
<p><a href="https://github.com/elixir-lsp/elixir-ls">Elixir LS</a> by Jake Becker (now maintained by the <a href="https://github.com/elixir-lsp">elixir-lsp</a> organization) is the language server implementation for Elixir.</p>
<h2><a href="#what-is-a-language-server" aria-hidden="true" class="anchor" id="what-is-a-language-server"></a>What is a language server?</h2>
<p>If you've been following the story of <a href="https://code.visualstudio.com">Visual Studio Code</a>, there is a chance you've heard of another recent creation from Microsoft: the <a href="https://langserver.org">Language Server Protocol</a>.</p>
<blockquote>
<p>The Language Server Protocol (LSP) defines the protocol used between an editor or IDE and a language server that provides language features like auto complete, go to definition, find all references etc.</p>
</blockquote>
<p>This allows creators to build universal "language servers" that can be used by any text editor.</p>
<h2><a href="#how-to-integrate-with-vim" aria-hidden="true" class="anchor" id="how-to-integrate-with-vim"></a>How to integrate with Vim</h2>
<p>Using a language server requires a client implementation for your editor and we are going to use <a href="https://github.com/dense-analysis/ale">ALE</a> by dense-analysis.</p>
<p>If you're using <a href="https://github.com/junegunn/vim-plug">vim-plug</a> you can install ALE by adding the following to your <code>.vimrc</code> and running <code>:PlugInstall</code>. Otherwise, consult your plugin manager's documentation.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-vim" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;">&quot; .vimrc</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">call</span> <span style="color: #8cf8f7;">plug#begin</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&#39;~/.vim/bundle&#39;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="4">...
</div><div class="line" data-line="5"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;dense-analysis/ale&#39;</span>
</div><div class="line" data-line="6"><span style="color: #e0e2ea; font-weight: bold;">call</span> <span style="color: #8cf8f7;">plug#end</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<p>Now let's install Elixir LS!</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ git clone git@github.com:elixir-lsp/elixir-ls.git
</div><div class="line" data-line="2">$ cd elixir-ls &amp;&amp; mkdir rel
</div><div class="line" data-line="3">
</div><div class="line" data-line="4"># checkout the latest release
</div><div class="line" data-line="5">$ git checkout tags/v0.4.0
</div><div class="line" data-line="6">
</div><div class="line" data-line="7">$ mix deps.get &amp;&amp; mix compile
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">$ mix elixir_ls.release -o rel
</div></code></pre>
<p>Perfect, our final step is to configure ALE to use Elixir LS.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-vim" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;">&quot; .vimrc</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #9b9ea4;">&quot; Required, explicitly enable Elixir LS</span>
</div><div class="line" data-line="4"><span style="color: #e0e2ea; font-weight: bold;">let</span> <span style="color: #e0e2ea;">g:</span><span style="color: #e0e2ea;">ale_linters</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">elixir</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&#39;elixir-ls&#39;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6"><span style="color: #9b9ea4;">&quot; Required, tell ALE where to find Elixir LS</span>
</div><div class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">let</span> <span style="color: #e0e2ea;">g:</span><span style="color: #e0e2ea;">ale_elixir_elixir_ls_release</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">expand</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;&lt;path to your release&gt;&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9"><span style="color: #9b9ea4;">&quot; Optional, you can disable Dialyzer with this setting</span>
</div><div class="line" data-line="10"><span style="color: #e0e2ea; font-weight: bold;">let</span> <span style="color: #e0e2ea;">g:</span><span style="color: #e0e2ea;">ale_elixir_elixir_ls_config</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #b3f6c0;">&#39;elixirLS&#39;</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #b3f6c0;">&#39;dialyzerEnabled&#39;</span><span style="color: #e0e2ea;">:</span> v:<span style="color: #e0e2ea;">false</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="11">
</div><div class="line" data-line="12"><span style="color: #9b9ea4;">&quot; Optional, configure as-you-type completions</span>
</div><div class="line" data-line="13"><span style="color: #e0e2ea; font-weight: bold;">set</span> <span style="color: #8cf8f7;">completeopt</span><span style="color: #e0e2ea;">=</span>menu,menuone,preview,noselect,noinsert
</div><div class="line" data-line="14"><span style="color: #e0e2ea; font-weight: bold;">let</span> <span style="color: #e0e2ea;">g:</span><span style="color: #e0e2ea;">ale_completion_enabled</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">1</span>
</div></code></pre>
<h2><a href="#now-what" aria-hidden="true" class="anchor" id="now-what"></a>Now what?</h2>
<p>I would familiarize yourself with the <a href="https://github.com/dense-analysis/ale#usage">features of ALE</a> and decide how you want to incorporate them into your workflow. ALE doesn't prescribe any keymappings, so feel free to experiment to see what works best for you!</p>
<p>Check out my <a href="https://github.com/mhanberg/.dotfiles/blob/fb9831367e5543aa84df15b0d1b08e8993c6a905/vimrc#L203..L232">.vimrc</a> to see how I use ALE.</p> ]]></description>
    </item>
    <item>
       <title>Announcing PlanetEx: an open source blog aggregator written in Elixir</title>
       <link>https://www.mitchellhanberg.com/post/2018/10/01/announcing-planetex-an-open-source-blog-aggregator-written-in-elixir/</link>
       <pubDate>Mon, 01 Oct 2018 13:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2018/10/01/announcing-planetex-an-open-source-blog-aggregator-written-in-elixir/</guid>
       <description><![CDATA[  ]]></description>
    </item>
    <item>
       <title>Elixir in Action: Book Review</title>
       <link>https://www.mitchellhanberg.com/post/2018/09/19/elixir-in-action-book-review/</link>
       <pubDate>Wed, 19 Sep 2018 09:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2018/09/19/elixir-in-action-book-review/</guid>
       <description><![CDATA[ <h2><a href="#who-is-the-target-audience" aria-hidden="true" class="anchor" id="who-is-the-target-audience"></a>Who is the target audience?</h2>
<p>The book is intended for beginner and intermediate Elixir developers. As the book progresses, the author dives into topics that get more complex, which might not be suitable unless you already understand the basics of Elixir.</p>
<h2><a href="#review" aria-hidden="true" class="anchor" id="review"></a>Review</h2>
<p><em>Elixir in Action</em> covers the full breadth of all that is Elixir, from the basic syntax to building distributed, fault tolerant, and scalable systems. This is definitely a lot, especially if <em>Elixir in Action</em> is the first book on Elixir you've read.</p>
<p>The book is broken into three parts: The Language, The Platform, and Production.</p>
<p>If this is your first foray into Elixir, I would suggest reading The Language and then putting the book down and write some code. You're going to be able to understand and absorb so much more from part 2 (The Platform) once you become comfortable with the syntax and the functional paradigm. The author provides numerous exercises to extend the examples he provides throughout the book.</p>
<p>The Platform dives into what really makes Elixir shine, which is the BEAM and the concurrency model that enables you to develop scalable and fault tolerant software. I would highly suggest working through the examples (or start a side project!) while reading this part. Reading concurrent code can be tough, so being able to see it <em>in action</em> will accelerate your understanding of how to develop software in Elixir.</p>
<p>Production covers the basics of how software in the Erlang ecosystem is generally packaged and deployed. This is possibly more involved than other languages you've worked in because Elixir allows you to do <em>hot code upgrades</em> without restarting the system.</p>
<p>I read <em>Elixir in Action</em> about a year into my Elixir journey and I definitely wished I would have read it in the beginning, but I still learned a lot and would recommend it to anyone interested in Elixir.</p> ]]></description>
    </item>
    <item>
       <title>How to Subscribe to SharePoint RSS Feeds Without NTLM Authentication</title>
       <link>https://www.mitchellhanberg.com/post/2018/08/19/how-to-subscribe-to-sharepoint-rss-feeds-without-ntlm-authentication/</link>
       <pubDate>Sat, 18 Aug 2018 21:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2018/08/19/how-to-subscribe-to-sharepoint-rss-feeds-without-ntlm-authentication/</guid>
       <description><![CDATA[  ]]></description>
    </item>
    <item>
       <title>Metaprogramming Elixir: Book Review</title>
       <link>https://www.mitchellhanberg.com/post/2018/08/18/metaprogramming-elixir-book-review/</link>
       <pubDate>Sat, 18 Aug 2018 09:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2018/08/18/metaprogramming-elixir-book-review/</guid>
       <description><![CDATA[ <h2><a href="#who-is-the-target-audience" aria-hidden="true" class="anchor" id="who-is-the-target-audience"></a>Who is the target audience?</h2>
<p>Advanced beginner to intermediate Elixir developers who have mastered the syntax and basic structure of Elixir applications who want to add advanced language features to their skill set.</p>
<h2><a href="#review" aria-hidden="true" class="anchor" id="review"></a>Review</h2>
<p>Chris McCord (creator of the <a href="https://phoenixframework.org/">Phoenix Framework</a>) introduces you to the power of macros with the Elixir programming language.</p>
<p>This book (a member of the <a href="https://pragprog.com/pragmatic-express">Pragmatic exPress</a> series) is short and to the point. If you're reading this book, you probably are interested in the practical application of macros and metaprogramming, and this book delivers.</p>
<p><em>Metaprogramming Elixir</em> guides you through the basics of defining macros, but quickly dives into best practices and how you can use these techniques to improve your real life code.</p>
<p>In addition to showing you when it's most appropriate to utilize a macro, Chris teaches you how to test macros and what aspects of metaprogramming are worth testing. As a practitioner of <a href="https://en.wikipedia.org/wiki/Test-driven_development">Test Driven Development</a>, I find this incredibly valuable.</p>
<p>I thoroughly enjoyed this book and will definitely be referencing it in the future.</p> ]]></description>
    </item>
    <item>
       <title>Container Components: Exploring State Management in React (Part I)</title>
       <link>https://www.mitchellhanberg.com/post/2018/07/25/exploring-state-management-in-react-container-components/</link>
       <pubDate>Wed, 25 Jul 2018 00:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2018/07/25/exploring-state-management-in-react-container-components/</guid>
       <description><![CDATA[ <blockquote>
<p>At what level of complexity will my React application require Redux?</p>
</blockquote>
<p>React developers have been asking this question for a long time, and answers still vary wildly. The truth is there is quite a bit we can do before needing to pull in Redux, and even then, <em><strong>Redux</strong></em> isn't our only option!</p>
<p>Even the creator of Redux, <a href="https://twitter.com/dan_abramov?lang=en">Dan Abramov</a> thinks that we might not need Redux (although, I think the spirit this statement applies to all 3rd-party libraries meant to help reduce complexity of state).</p>
<blockquote>
<p><a href="https://t.co/3zBPrbhFeL">You Might Not Need Redux</a></p>
<p>— <a href="@dan_abramov">Dan Abramov</a> (<a href="https://twitter.com/dan_abramov/status/777983404914671616?ref_src=twsrc%5Etfw">September 19, 2016</a>)</p>
</blockquote>
<p>In this series, we'll explore a few different patterns you can introduce to your code base before reaching for a 3rd party solution!</p>
<h2><a href="#containerpresenter-components" aria-hidden="true" class="anchor" id="containerpresenter-components"></a>Container/Presenter Components</h2>
<p>This pattern separates what might be a single component into two: a Container component to maintain state and a Presenter component to render visual markup.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-javascript" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">const</span> <span style="color: #8cf8f7;">Cart</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">props</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">ul</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">&lt;</span>h2<span style="color: #8cf8f7;">&gt;</span><span style="color: #e0e2ea; font-weight: bold;">Cart</span><span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">h2</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="4">    <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">input</span>
</div><div class="line" data-line="5">      <span style="color: #8cf8f7;">type</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">&quot;text&quot;</span>
</div><div class="line" data-line="6">      <span style="color: #8cf8f7;">onChange</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">props</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">onNewItemChange</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="7">      <span style="color: #8cf8f7;">value</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">props</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">newItem</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="8">    <span style="color: #8cf8f7;">/&gt;</span>
</div><div class="line" data-line="9">    <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">button</span> <span style="color: #8cf8f7;">onClick</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">props</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">addItem</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #8cf8f7;">&gt;</span>Add item<span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">button</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11">    <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">props</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">idx</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="12">      <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">li</span> <span style="color: #8cf8f7;">key</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">idx</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="13">        <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">button</span> <span style="color: #8cf8f7;">onClick</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">props</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">removeItem</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #8cf8f7;">&gt;</span>Remove<span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">button</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="14">        <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">&rbrace;</span>, <span style="color: #8cf8f7;">&amp;ensp;</span>
</div><div class="line" data-line="15">        <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">props</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">discounts</span><span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">idx</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">&rbrace;</span>% off!
</div><div class="line" data-line="16">      <span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">li</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="17">    <span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="18">  <span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">ul</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="19"><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="20">
</div><div class="line" data-line="21"><span style="color: #e0e2ea; font-weight: bold;">class</span> <span style="color: #e0e2ea;">CartContainer</span> <span style="color: #e0e2ea; font-weight: bold;">extends</span> <span style="color: #e0e2ea;">React</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">Component</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="22">  <span style="color: #e0e2ea;">state</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="23">    <span style="color: #e0e2ea;">newItem</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">&quot;&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="24">    <span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="25">    <span style="color: #e0e2ea;">discounts</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="26">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="27">
</div><div class="line" data-line="28">  <span style="color: #8cf8f7;">componentDidUpdate</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">prevProps</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">prevState</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="29">    <span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">prevState</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">length</span> <span style="color: #e0e2ea;">!==</span> <span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">length</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="30">      <span style="color: #e0e2ea; font-weight: bold;">const</span> <span style="color: #e0e2ea;">itemsQuery</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">join</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;,&quot;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="31">
</div><div class="line" data-line="32">      <span style="color: #8cf8f7;">ajax</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">`https://discountdb.com/?items=<span style="color: #8cf8f7;">$&lbrace;</span><span style="color: #e0e2ea;">itemsQuery</span><span style="color: #8cf8f7;">&rbrace;</span>`</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="33">        <span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">then</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">discounts</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">setState</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">discounts</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="34">    <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="35">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="36">
</div><div class="line" data-line="37">  <span style="color: #e0e2ea;">onNewItemChange</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">event</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">setState</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span> <span style="color: #e0e2ea;">newItem</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">event</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">target</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">value</span> <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="38">
</div><div class="line" data-line="39">  <span style="color: #e0e2ea;">addItem</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="40">    <span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">setState</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">prevState</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="41">      <span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">...</span><span style="color: #e0e2ea;">prevState</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">prevState</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">newItem</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="42">      <span style="color: #e0e2ea;">newItem</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">&quot;&quot;</span>
</div><div class="line" data-line="43">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="44">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="45">
</div><div class="line" data-line="46">  <span style="color: #e0e2ea;">removeItem</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">item</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="47">    <span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">setState</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">prevState</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="48">      <span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">prevState</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">filter</span><span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="49">        <span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">i</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">idx</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">prevItems</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #e0e2ea;">idx</span> <span style="color: #e0e2ea;">!==</span> <span style="color: #e0e2ea;">prevItems</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">indexOf</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="50">      <span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="51">    <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="52">  <span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="53">
</div><div class="line" data-line="54">  <span style="color: #8cf8f7;">render</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="55">    <span style="color: #e0e2ea; font-weight: bold;">return</span> <span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="56">      <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">div</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="57">        <span style="color: #8cf8f7;">&lt;</span><span style="color: #8cf8f7;">Cart</span>
</div><div class="line" data-line="58">          <span style="color: #8cf8f7;">newItem</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">newItem</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="59">          <span style="color: #8cf8f7;">onNewItemChange</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">onNewItemChange</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="60">          <span style="color: #8cf8f7;">items</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="61">          <span style="color: #8cf8f7;">discounts</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">state</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">discounts</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="62">          <span style="color: #8cf8f7;">addItem</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">addItem</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="63">          <span style="color: #8cf8f7;">removeItem</span><span style="color: #e0e2ea;">=</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">this</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">removeItem</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="64">        <span style="color: #8cf8f7;">/&gt;</span>
</div><div class="line" data-line="65">      <span style="color: #8cf8f7;">&lt;/</span><span style="color: #8cf8f7;">div</span><span style="color: #8cf8f7;">&gt;</span>
</div><div class="line" data-line="66">    <span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">;</span>
</div><div class="line" data-line="67">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="68"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<p><a href="https://codesandbox.io/s/wo7y9voowk"><img src="https://codesandbox.io/static/img/play-codesandbox.svg" alt="Edit wo7y9voowk" /></a></p>
<p>Here we can see that we have a Container component, <code>CartContainer</code>, that handles controlling component state with the <code>addItem</code>, <code>removeItem</code>, and <code>onNewItemChange</code> callbacks, and fetching a list of discounts from an external REST api. This enables us to write <code>Cart</code>, our Presenter component, as a Pure Functional component.</p>
<p>After extracting a Container and a Presenter from one of our bigger components, we might find the Container to still be fairly large, or handling several concerns, potentially signaling that we can break down our Container even further.</p>
<p>In our case, we might extract a <code>DiscountContainer</code> from <code>CartContainer</code> to segregate the logic of maintaining the contents of the cart from the logic of fetching the discounts for those items.</p>
<p>The hierarchy of this would look like <code>CartContainer</code> -> <code>DiscountContainer</code> -> <code>Cart</code>, having <code>CartContainer</code> pass the discount-less items to the <code>DiscountContainer</code>, which will fetch the discounts and then pass the now discounted items to the <code>Cart</code>.</p>
<h2><a href="#benefits" aria-hidden="true" class="anchor" id="benefits"></a>Benefits</h2>
<p>Partitioning our components on their state boundary will help reduce complexity by simply having less code to work with at a time, while still staying "inside React".</p>
<p>I think this pattern really starts to pay dividends when we have a lifecycle method, like <code>componentDidUpdate</code>, doing a lot of asynchronous work (like making HTTP requests). Given the asynchronous nature, this sort of code tends to be very difficult to test (with both automated unit testing and manual testing), so breaking this stateful logic into separate components helps keeps us sane and our code focused.</p>
<p>It's helpful to remind ourselves that when unit testing a React component, we are essentially testing the <code>render</code> function. Given the inputs (<code>props</code>), what is the output? You've probably noticed tests are painful to write if there is a lot of setup, especially if the setup is required for a feature of the component that you aren't even testing.</p>
<p>Keeping our components small and focused will go a long way for keeping ourselves happy and productive!</p>
<h2><a href="#drawbacks" aria-hidden="true" class="anchor" id="drawbacks"></a>Drawbacks</h2>
<p>While the Container/Presenter pattern is not always one-for-one (<code>Cart</code> <-> <code>CartContainer</code>), you will encounter a lot of similarly named components. This can sometimes cause a communication breakdown amongst the team, as you will trip over your own words attempting to say things like "The CartContainer passes the products to the Cart which then passes them to the Checkout component, or is it the CheckoutContainer component?".</p>
<p>If you can think of better names, I would suggest using them! Your code will still be following the pattern even if they don't have the word Container in the name. 😏</p>
<h2><a href="#wrapping-up" aria-hidden="true" class="anchor" id="wrapping-up"></a>Wrapping Up</h2>
<p>My team has utilized this pattern heavily, and I believe it is a solid option to consider before reaching for a tool like Redux.</p>
<p>If you've never heard of this pattern and would like to learn more, Dan Abramov <a href="https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0">has also written about this topic</a>.</p>
<p>Now try this in your codebase or introduce the idea to your team during a lunch-and-learn and let me know how it goes!</p> ]]></description>
    </item>
    <item>
       <title>Building with Elm at SEP:Makes</title>
       <link>https://www.mitchellhanberg.com/post/2018/03/08/building-elm-at-sep-makes/</link>
       <pubDate>Thu, 08 Mar 2018 09:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2018/03/08/building-elm-at-sep-makes/</guid>
       <description><![CDATA[  ]]></description>
    </item>
    <item>
       <title>Integrate and Deploy React with Phoenix</title>
       <link>https://www.mitchellhanberg.com/post/2018/02/22/integrate-and-deploy-react-with-phoenix/</link>
       <pubDate>Thu, 22 Feb 2018 08:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2018/02/22/integrate-and-deploy-react-with-phoenix/</guid>
       <description><![CDATA[ <blockquote>
<p>You've just finished your lightning fast Phoenix JSON API, what's next?</p>
</blockquote>
<h2><a href="#motivation" aria-hidden="true" class="anchor" id="motivation"></a>Motivation</h2>
<p><img src="/images/contact.png" alt="" /></p>
<p>My most recent side project, <a href="https://www.github.com/mhanberg/contact">Contact</a>, is a JSON REST API written with <a href="https://elixir-lang.org/">Elixir</a> and <a href="https://github.com/phoenixframework/phoenix">Phoenix</a>, designed to be the backend to an instant messaging application (e.g., Slack). There was a hackathon coming up at work, and I thought it'd be fun to make a frontend for Contact during it, and although Contact's development was thoroughly test-driven, I wanted to make sure that my API was ready to be used.</p>
<p>The hackathon was two weeks away, so I needed to quickly prototype a UI to flesh out any oversights I made.</p>
<p>I decided to go with React over basic server-rendered html templates because my next project at work will be using React and figured I could use this to level up my skills.</p>
<h2><a href="#lets-add-react" aria-hidden="true" class="anchor" id="lets-add-react"></a>Let's add React!</h2>
<p>If you generated your Phoenix project using the <code>mix phx.new --no-html --no-brunch</code> command, you're good to go!</p>
<p>If not, let's rip out the stock html and Javascript scaffolding that Phoenix generates for you. You can safely remove the <code>priv/assets</code> directory (which contains all of the Brunch configuration) and <code>lib/&lt;path to your web directory&gt;/templates</code>, along with any Phoenix views, controllers, and routes that you aren't using.</p>
<p>A good place to install our React app is the <code>priv</code> directory, so let's move into there and run the installer.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1"># priv/
</div><div class="line" data-line="2">$ npx create-react-app contact-react 
</div><div class="line" data-line="3">$ cd contact-react 
</div><div class="line" data-line="4">$ yarn start # npm start if you don&#39;t use yarn
</div></code></pre>
<p><a href="https://github.com/facebook/create-react-app">create-react-app</a> gets us set up with React, Babel, and Webpack out of the box, allowing us to get started developing our application and not mess around with a ton of esoteric configuration.</p>
<h2><a href="#success" aria-hidden="true" class="anchor" id="success"></a>Success!</h2>
<p>We now have a development server running, serving the generated React application.</p>
<p><img src="/images/create-react-default.png" alt="" /></p>
<h2><a href="#connecting-the-frontend-to-the-backend" aria-hidden="true" class="anchor" id="connecting-the-frontend-to-the-backend"></a>Connecting the frontend to the backend</h2>
<p>You may have noticed that your Phoenix server and the React development server are running on two different ports, how can we allow our two applications to communicate with each other?</p>
<h3><a href="#development" aria-hidden="true" class="anchor" id="development"></a>Development</h3>
<p>We'll set up a proxy for development, so we won't have to specify the absolute URI of the API endpoints we want to hit. Let's add this line to our <code>package.json</code>.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-json" translate="no" tabindex="0"><div class="line" data-line="1">&quot;proxy&quot;<span style="color: #e0e2ea;">:</span> &quot;http://localhost:4000&quot;
</div></code></pre>
<p>This line will set all network request URIs to be made relative to our Phoenix server.</p>
<h3><a href="#production" aria-hidden="true" class="anchor" id="production"></a>Production</h3>
<p>In production, we'll have the Phoenix server send the frontend to the client.</p>
<p>To accomplish this, we'll set up the root endpoint to serve the contents of the <code>build</code> directory of our React app.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;"># endpoint.ex</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #8cf8f7;">plug</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">Plug.Static.IndexHtml</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">at: </span><span style="color: #b3f6c0;">&quot;/&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="4"><span style="color: #8cf8f7;">plug</span><span style="color: #e0e2ea;">(</span>
</div><div class="line" data-line="5">  <span style="color: #e0e2ea;">Plug.Static</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="6">  <span style="color: #8cf8f7;">at: </span><span style="color: #b3f6c0;">&quot;/&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="7">  <span style="color: #8cf8f7;">from: </span><span style="color: #b3f6c0;">&quot;priv/contact-react/build/&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="8">  <span style="color: #8cf8f7;">only: </span><span style="color: #8cf8f7;">~</span>w<span style="color: #e0e2ea;">(</span>index.html favicon.ico static service-worker.js<span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<p><a href="https://hex.pm/packages/plug_static_index_html"><code>Plug.Static.IndexHtml</code></a> will allow us to serve the <code>index.html</code> that Webpack generates from the root endpoint.</p>
<p>Now if we run <code>yarn build</code>, start our Phoenix server, and navigate to <code>localhost:4000</code> in the browser, we should see our React application!</p>
<h2><a href="#deployment" aria-hidden="true" class="anchor" id="deployment"></a>Deployment</h2>
<p>Since we have added another build step to our workflow, we'll need to include that in our deployment process. I will describe the steps needed to deploy using <a href="https://travis-ci.org/">Travis CI</a>.</p>
<p>I added a <code>before_deploy</code> step and set the <code>skip_cleanup</code> flag to the <code>deploy</code> step to my <code>.travis.yml</code> file, resembling the following.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-yaml" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #a6dbff;">before_deploy</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="2">  <span style="color: #e0e2ea;">-</span> <span style="color: #b3f6c0;">rm -rf ~/.nvm &amp;&amp; curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash &amp;&amp; nvm install node &amp;&amp; nvm use node</span>
</div><div class="line" data-line="3">  <span style="color: #e0e2ea;">-</span> <span style="color: #b3f6c0;">cd priv/contact-react &amp;&amp; yarn install &amp;&amp; yarn build &amp;&amp; cd -</span> 
</div><div class="line" data-line="4"><span style="color: #a6dbff;">deploy</span><span style="color: #e0e2ea;">:</span>
</div><div class="line" data-line="5">  <span style="color: #a6dbff;">skip_cleanup</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">true</span>
</div></code></pre>
<p>A breakdown of what is happening here:</p>
<ul>
<li>Reinstall the latest version of <a href="https://github.com/creationix/nvm">Node Version Manager</a>.</li>
<li>Install the latest version of Node.js.</li>
<li>Set the current version of Node.js to the one we just installed.</li>
<li>Build our React application (Yarn is already installed).</li>
<li>Tell Travis to deploy the compiled React application (otherwise, Travis would see the <code>build</code> directory as a build artifact and clean it up before deployment).</li>
</ul>
<h2><a href="#its-time-to-get-to-work" aria-hidden="true" class="anchor" id="its-time-to-get-to-work"></a>It's time to get to work!</h2>
<p>In 10 minutes we've gone from nothing to a deployed application!</p>
<p>Following these steps allowed me to get right to business; I was successful in prototyping 90% of my application before the hackathon.</p>
<hr />
<h4><a href="#references" aria-hidden="true" class="anchor" id="references"></a>References</h4>
<ul>
<li><a href="http://www.petecorey.com/blog/2017/04/03/using-create-react-app-with-phoenix/">http://www.petecorey.com/blog/2017/04/03/using-create-react-app-with-phoenix/</a></li>
<li><a href="https://docs.travis-ci.com/user/deployment/heroku/#Deploying-build-artifacts">https://docs.travis-ci.com/user/deployment/heroku/#Deploying-build-artifacts</a></li>
<li><a href="https://docs.travis-ci.com/user/deployment/heroku/#Running-commands-before-and-after-deploy">https://docs.travis-ci.com/user/deployment/heroku/#Running-commands-before-and-after-deploy</a></li>
</ul> ]]></description>
    </item>
    <item>
       <title>Version Your Dotfiles for Great Good</title>
       <link>https://www.mitchellhanberg.com/post/2018/01/29/version-your-dotfiles-for-great-good/</link>
       <pubDate>Mon, 29 Jan 2018 09:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2018/01/29/version-your-dotfiles-for-great-good/</guid>
       <description><![CDATA[ <h2><a href="#what-are-dotfiles" aria-hidden="true" class="anchor" id="what-are-dotfiles"></a>What are dotfiles?</h2>
<p>Your dotfiles are the hidden files or folders that live in your home directory, for example your <code>.vimrc</code> and your <code>.bashrc</code>.</p>
<h2><a href="#what-do-i-mean-by-version" aria-hidden="true" class="anchor" id="what-do-i-mean-by-version"></a>What do I mean by version?</h2>
<p>By "version", I mean to track your dotfiles using a version control system, like git, and a hosting service, like Github.</p>
<h2><a href="#why-would-you-want-to-version-them" aria-hidden="true" class="anchor" id="why-would-you-want-to-version-them"></a>Why would you want to version them?</h2>
<p>Versioning your dotfiles allows you to track them and to be able to share them between computers, making it easy to provision a new computer.</p>
<h2><a href="#lets-get-started" aria-hidden="true" class="anchor" id="lets-get-started"></a>Let's get started</h2>
<p>There's a good chance that you arleady have some dotfiles, don't worry, it's easy to start tracking them. Our first order of business is to install a handy suite of tools called <a href="https://github.com/thoughtbot/rcm">rcm</a>, which abstracts the process of symlinking our dotfiles.</p>
<p>On macOS we would install via <a href="https://brew.sh/">Homebrew</a>.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ brew install rcm
</div></code></pre>
<p>rcm expects a <code>~/.dotfiles</code> directory, so let's create that and initialize our git repository.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ mkdir ~/.dotfiles &amp;&amp; cd ~/.dotfiles &amp;&amp; git init
</div></code></pre>
<p>At this point, if you have any dotfiles (you probably do), you'll want to add them to your <code>~/.dotfiles</code> directory, and for this, we use the <a href="http://thoughtbot.github.io/rcm/mkrc.1.html">mkrc</a> tool provided by rcm.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ mkrc .vimrc
</div><div class="line" data-line="2">$ mkrc .zshrc
</div><div class="line" data-line="3">$ mkrc .rubocop.yml
</div></code></pre>
<p>At this point, rcm has copied these files to your <code>~/.dotfiles</code> directory with the dot stripped from the name (<code>.vimrc -&gt; vimrc</code>) and replaced the copy in your home directory with a <a href="https://en.wikipedia.org/wiki/Symbolic_link">symlink</a> to the new file in your <code>~/.dotfiles</code> directory.</p>
<h3><a href="#what-about-my-vim-plugins" aria-hidden="true" class="anchor" id="what-about-my-vim-plugins"></a>What about my vim plugins?</h3>
<p>For most vim plugin managers, you are probably going to be cloning at least one git repository somewhere in your <code>~/.vim</code> directory. If you don't plan on ever updating these repos, you can go ahead and run <code>mkrc .vim</code>.</p>
<p>If you want to be able to update these plugins (or any other tool you use which relies on a git repo), you'll need to use <a href="http://www.vogella.com/tutorials/GitSubmodules/article.html">git submodules</a>. I attempted to turn my existing vim plugins into submodules within my <code>.dotfiles</code> repository, but I wasn't successful. So instead of re-cloning each plugin as a submodule, I decided to switch to a vim plugin manager that doesn't require manually cloning repos.</p>
<p>I opted to start using <a href="https://github.com/junegunn/vim-plug">vim-plug</a> over <a href="https://github.com/tpope/vim-pathogen">pathogen</a>, which requires less boilerplate and all of the configuration goes in your <code>.vimrc</code>.</p>
<p>Once I was done, the section of my <code>.vimrc</code> handling my vim plugins looked like so:</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-vim" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;">&quot; Install vim-plug and plugins if vim-plug is not already installed</span>
</div><div class="line" data-line="2"><span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #8cf8f7;">empty</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">glob</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&#39;~/.vim/autoload/plug.vim&#39;</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="3">  <span style="color: #e0e2ea; font-weight: bold;">silent</span> !curl -fLo ~/.vim/autoload/plug.vim --create-dirs
</div><div class="line" data-line="4">    \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
</div><div class="line" data-line="5">  <span style="color: #e0e2ea; font-weight: bold;">autocmd</span> <span style="color: #e0e2ea;">VimEnter</span> <span style="color: #e0e2ea;">*</span> <span style="color: #8cf8f7;">PlugInstall</span> --sync | <span style="color: #e0e2ea; font-weight: bold;">source</span> <span style="color: #8cf8f7;">$MYVIMRC</span>
</div><div class="line" data-line="6"><span style="color: #e0e2ea; font-weight: bold;">endif</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8"><span style="color: #9b9ea4;">&quot; Plugins</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">call</span> <span style="color: #8cf8f7;">plug#begin</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&#39;~/.vim/bundle&#39;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="10"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;slashmili/alchemist.vim&#39;</span>
</div><div class="line" data-line="11"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;kien/ctrlp.vim&#39;</span>
</div><div class="line" data-line="12"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;vim-airline/vim-airline&#39;</span>
</div><div class="line" data-line="13"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;tpope/vim-bundler&#39;</span>
</div><div class="line" data-line="14"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;tpope/vim-dispatch&#39;</span>
</div><div class="line" data-line="15"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;elixir-editors/vim-elixir&#39;</span>
</div><div class="line" data-line="16"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;tpope/vim-endwise&#39;</span>
</div><div class="line" data-line="17"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;tpope/vim-fugitive&#39;</span>
</div><div class="line" data-line="18"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;airblade/vim-gitgutter&#39;</span>
</div><div class="line" data-line="19"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;JamshedVesuna/vim-markdown-preview&#39;</span>
</div><div class="line" data-line="20"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;tpope/vim-rails&#39;</span>
</div><div class="line" data-line="21"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;tpope/vim-sensible&#39;</span>
</div><div class="line" data-line="22"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;janko-m/vim-test&#39;</span>
</div><div class="line" data-line="23"><span style="color: #8cf8f7;">Plug</span> <span style="color: #b3f6c0;">&#39;christoomey/vim-tmux-navigator&#39;</span>
</div><div class="line" data-line="24"><span style="color: #e0e2ea; font-weight: bold;">call</span> <span style="color: #8cf8f7;">plug#end</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
</div></code></pre>
<h3><a href="#yeah-but-what-about-the-submodules-i-cant-avoid-making" aria-hidden="true" class="anchor" id="yeah-but-what-about-the-submodules-i-cant-avoid-making"></a>Yeah, but what about the submodules I can't avoid making?</h3>
<p>Thought you might ask that, I encountered the same challenge. I use <a href="https://github.com/robbyrussell/oh-my-zsh">oh-my-zsh</a> for <a href="https://en.wikipedia.org/wiki/Z_shell">zsh</a> customizations (mostly the themes¯\_(ツ)_/¯), which relies on a git repository in your home directory <code>.oh-my-zsh</code>.</p>
<p>While I wasn't able to transfer the existing directory to my new <code>.dotfiles</code> directory, I was able to reclone it as a git submodule.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ cd ~/.dotfiles
</div><div class="line" data-line="2">$ git submodule add -b master &lt;repo url&gt;
</div><div class="line" data-line="3">$ git submodule init # you should only have to run this the first time
</div><div class="line" data-line="4">$ rcup -v
</div></code></pre>
<p>The submodule is set up to track the master branch, has been cloned into your repository, and a symlink to your home directory has been made using the <a href="http://thoughtbot.github.io/rcm/rcup.1.html">rcup</a> tool provided by rcm.</p>
<p>If you wish to update it at any time, you only need to move into the directory of the submodule and do a <code>git pull</code>, followed by  returning to the parent repository and committing the change.</p>
<h2><a href="#final-steps" aria-hidden="true" class="anchor" id="final-steps"></a>Final Steps</h2>
<p>Now that you have all of your dotfiles in a git repository, it's time to push that repository to Github and figure out how you are going to install these dotfiles the next time you are setting up a computer.</p>
<h3><a href="#provisioning-a-new-machine-with-your-dotfiles" aria-hidden="true" class="anchor" id="provisioning-a-new-machine-with-your-dotfiles"></a>Provisioning a new machine with your dotfiles</h3>
<p>This is the part we've been working towards, and thanks to our efforts, this part is a breeze.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1"># Install git if not installed
</div><div class="line" data-line="2"># Install rcm
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">$ git clone --recursive &lt;url&gt; ~/.dotfiles
</div><div class="line" data-line="5">$ rcup -v
</div></code></pre>
<p>Voila!</p>
<p>If you want to make it even easier to install, rcm provides a utlity to generate a standalone install script.</p>
<blockquote>
<p>The rcup(1) tool can be used to generate a portable shell script. Instead of running a command such as ln(1) or rm(1), it will print the command to stdout. This is controlled with the -g flag. Note that this will generate a script to create an exact replica of the synchronization, including tags, host-specific files, and dotfiles directories.</p>
<p><code>env RCRC=/dev/null rcup -B 0 -g &gt; install.sh</code></p>
<p>Using the above command, you can now run install.sh to install (or re-install) your rc files. The install.sh script can be stored in your dotfiles directory, copied between computers, and so on.</p>
</blockquote>
<h2><a href="#wrapping-up" aria-hidden="true" class="anchor" id="wrapping-up"></a>Wrapping Up</h2>
<p>If you've followed along and have made it this far, congratulations! You now have a backup of all of your configuration files!</p>
<p>If you started reading this, but thought, "Dang, I don't have any dotfiles to version, why am I reading this?", you're in luck! Since versioning one's dotfiles is a common practice, there are plenty of repositories out there for which you can choose to fork and start your own collection.</p>
<p>For example, if you were to checkout <a href="https://github.com/mhanberg/.dotfiles">my dotfiles</a> you would find a pretty basic setup for <a href="https://www.ruby-lang.org/en/">ruby</a> and <a href="https://elixir-lang.org/">elixir</a> development, along with helpful configurations for <a href="https://github.com/tmux/tmux/wiki">tmux</a> and <a href="http://www.vim.org/">vim</a>, which would be a good starting point if you didn't want to curate your dotfiles from scratch.</p> ]]></description>
    </item>
    <item>
       <title>Implementing API Authentication with Guardian in Phoenix</title>
       <link>https://www.mitchellhanberg.com/post/2017/11/28/implementing-api-authentication-with-guardian/</link>
       <pubDate>Tue, 28 Nov 2017 09:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2017/11/28/implementing-api-authentication-with-guardian/</guid>
       <description><![CDATA[ <p>Most applications need some sort of authentication and authorization, and REST API's are no different. If you are familiar with web development but have never worked on one that does not have a front end (like me), then the authentication functionality might stump you at first.</p>
<h2><a href="#what-is-guardian" aria-hidden="true" class="anchor" id="what-is-guardian"></a>What is Guardian?</h2>
<blockquote>
<p>Guardian is a token based authentication library for use with Elixir applications.</p>
</blockquote>
<ul>
<li>More can be learned by reading its <a href="https://github.com/ueberauth/guardian">documentation</a>, which I highly recommend.</li>
<li>Keep in mind that the "tokens" that Guardians refers to are <a href="https://jwt.io/introduction/">JSON Web Tokens</a>.</li>
</ul>
<h2><a href="#installation-and-configuration" aria-hidden="true" class="anchor" id="installation-and-configuration"></a>Installation and Configuration</h2>
<p>Add Guardian 1.0 as a dependency in <code>mix.exs</code>.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:guardian</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;~&gt; 1.0&quot;</span><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<p>And install</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ mix deps.get
</div></code></pre>
<p>Guardian requires that you add an implementation module, I added mine to an <code>auth</code> folder in my web directory.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;"># lib/my_app_web/auth/guardian.ex</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">MyAppWeb.Guardian</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">use</span> <span style="color: #e0e2ea;">Guardian</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">otp_app: </span><span style="color: #8cf8f7;">:my_app</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">subject_for_token</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">resource</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_claims</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="7">    <span style="color: #9b9ea4;"># You can use any value for the subject of your token but</span>
</div><div class="line" data-line="8">    <span style="color: #9b9ea4;"># it should be useful in retrieving the resource later, see</span>
</div><div class="line" data-line="9">    <span style="color: #9b9ea4;"># how it being used on `resource_from_claims/1` function.</span>
</div><div class="line" data-line="10">    <span style="color: #9b9ea4;"># A unique `id` is a good subject, a non-unique email address</span>
</div><div class="line" data-line="11">    <span style="color: #9b9ea4;"># is a poor subject.</span>
</div><div class="line" data-line="12">
</div><div class="line" data-line="13">    <span style="color: #e0e2ea;">sub</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">to_string</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">resource</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">id</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="14">    <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">sub</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="15">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="16">
</div><div class="line" data-line="17">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">resource_from_claims</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">claims</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="18">    <span style="color: #9b9ea4;"># Here we&#39;ll look up our resource from the claims, the subject can be</span>
</div><div class="line" data-line="19">    <span style="color: #9b9ea4;"># found in the `&quot;sub&quot;` key. In `above subject_for_token/2` we returned</span>
</div><div class="line" data-line="20">    <span style="color: #9b9ea4;"># the resource id so here we&#39;ll rely on that to look it up.</span>
</div><div class="line" data-line="21">
</div><div class="line" data-line="22">    <span style="color: #e0e2ea;">id</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">claims</span><span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;sub&quot;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="23">    <span style="color: #e0e2ea;">resource</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">MyApp</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">get_resource_by_id</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">id</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="24">    <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span>  <span style="color: #e0e2ea;">resource</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="25">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="26"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>There are two functions that you are required to implement, <code>subject_for_token/2</code> and <code>resource_for_claims/2</code>. The compiler will complain if they are not implemented.</p>
<p>The <code>resource</code> refers to a user, and the <code>sub</code> (subject) is the user's primary key (or another unique identifier). The function <code>MyApp.get_resource_by_id(id)</code>, that is called in <code>resource_from_claims/2</code>, is just a placeholder. You will need to implement that function (the name is not important, it can be whatever you want) and it should retrieve the user based on the <code>sub</code> (subject).</p>
<ul>
<li>This is taken directly from Guardian's example code.</li>
<li>More can be learned about claims by reading the <a href="https://jwt.io/introduction/">JSON Web Token documentation</a></li>
</ul>
<p>Finally, we add a config to <code>config.exs</code></p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">config</span> <span style="color: #8cf8f7;">:my_app</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">MyAppWeb.Guardian</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="2">       <span style="color: #8cf8f7;">issuer: </span><span style="color: #b3f6c0;">&quot;my_app&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">       <span style="color: #8cf8f7;">secret_key: </span><span style="color: #b3f6c0;">&quot;Secret key. You can use `mix guardian.gen.secret` to get one&quot;</span>
</div></code></pre>
<p>You will need to create a secret key using the method above. If you're writing a production application, you should use an environment variable.</p>
<ul>
<li>The atom <code>:my_app</code> corresponds to the atom in the implementation module and the namespacing of the module also corresponds to the implementation module.</li>
<li>The value for the key <code>issuer</code> can be whatever you want.</li>
</ul>
<h2><a href="#authentication" aria-hidden="true" class="anchor" id="authentication"></a>Authentication</h2>
<p>Our next step will be to perform the authentication of the credentials that the client has sent to our API.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">authenticate</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">user: </span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">password: </span><span style="color: #e0e2ea;">password</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #9b9ea4;"># Does password match the one stored in the database?</span>
</div><div class="line" data-line="3">  <span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #e0e2ea;">Comeonin.Bcrypt</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">checkpw</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">password</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">password_digest</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="4">    <span style="color: #e0e2ea;">true</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="5">      <span style="color: #9b9ea4;"># Yes, create and return the token</span>
</div><div class="line" data-line="6">      <span style="color: #e0e2ea;">MyAppWebWeb.Guardian</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">encode_and_sign</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="7">    <span style="color: #9b9ea4;">_</span> <span style="color: #e0e2ea;">-&gt;</span>
</div><div class="line" data-line="8">      <span style="color: #9b9ea4;"># No, return an error</span>
</div><div class="line" data-line="9">      <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:error</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:unauthorized</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="10">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="11"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>This is my authentication function, it takes the User struct that I located in the database based on the unique identifier that was passed by the request (in my case can be either an email address or a username) and the password attempt.</p>
<p>I am using the password hashing library <a href="https://github.com/riverrun/comeonin">Comeonin</a> to hash my passwords, so here I use the <code>checkpw/2</code> function to check the password attempt against the digest (AKA a hash) stored in the database. This function returns either true or false, so if the password is correct, we fall into the true case and we create our token (JSON Web Token, or JWT) and return it.</p>
<p>Otherwise we return the tuple <code>&lbrace;:error, :unauthorized&rbrace;</code> to signify that the authentication attempt failed.</p>
<h2><a href="#the-controller" aria-hidden="true" class="anchor" id="the-controller"></a>The Controller</h2>
<p>Let's expose this functionality to our public API by making a controller endpoint to sign in a user.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">sign_in</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">params</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #9b9ea4;"># Find the user in the database based on the credentials sent with the request</span>
</div><div class="line" data-line="3">  <span style="color: #e0e2ea; font-weight: bold;">with</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">User</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">user</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">Accounts</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">find</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">params</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">email</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="4">    <span style="color: #9b9ea4;"># Attempt to authenticate the user</span>
</div><div class="line" data-line="5">    <span style="color: #e0e2ea; font-weight: bold;">with</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">token</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_claims</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">&lt;-</span> <span style="color: #e0e2ea;">Accounts</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">authenticate</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">user: </span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">password: </span><span style="color: #e0e2ea;">login_cred</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">password</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="6">      <span style="color: #9b9ea4;"># Render the token</span>
</div><div class="line" data-line="7">      <span style="color: #8cf8f7;">render</span> <span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;token.json&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">token: </span><span style="color: #e0e2ea;">token</span>
</div><div class="line" data-line="8">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="9">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="10"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>Here we find the user in the database and authenticate the user. If the authentication is successful, we render the token back to the consumer.</p>
<p>Note: Here I am using the <code>with</code> syntax along with Phoenix's <a href="http://phoenixframework.org/blog/phoenix-1-3-0-released"><code>action_fallback</code></a> functionality.</p>
<h2><a href="#authorization" aria-hidden="true" class="anchor" id="authorization"></a>Authorization</h2>
<p>In the context of a web application, this is the process of fencing off most of the routes from unauthenticated visitors. However, there are two routes that should <em><strong>not</strong></em> be fenced off, the route to sign in a user and the route to create a user.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;"># router.ex</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #8cf8f7;">pipeline</span> <span style="color: #8cf8f7;">:api</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="4">  <span style="color: #8cf8f7;">plug</span> <span style="color: #8cf8f7;">:accepts</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">[</span><span style="color: #b3f6c0;">&quot;json&quot;</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="5"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7"><span style="color: #8cf8f7;">pipeline</span> <span style="color: #8cf8f7;">:api_auth</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="8">  <span style="color: #8cf8f7;">plug</span> <span style="color: #e0e2ea;">MyAppWeb.Guardian.AuthPipeline</span>
</div><div class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11"><span style="color: #8cf8f7;">scope</span> <span style="color: #b3f6c0;">&quot;/api&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">MyAppWeb.Api</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="12">  <span style="color: #8cf8f7;">pipe_through</span> <span style="color: #8cf8f7;">:api</span>
</div><div class="line" data-line="13">
</div><div class="line" data-line="14">  <span style="color: #8cf8f7;">resources</span> <span style="color: #b3f6c0;">&quot;/users&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">UserController</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">only: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">:create</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="15">  <span style="color: #8cf8f7;">post</span> <span style="color: #b3f6c0;">&quot;/users/sign_in&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">UserController</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:sign_in</span>
</div><div class="line" data-line="16"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="17">
</div><div class="line" data-line="18"><span style="color: #8cf8f7;">scope</span> <span style="color: #b3f6c0;">&quot;/api&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">MyAppWeb.Api</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="19">  <span style="color: #8cf8f7;">pipe_through</span> <span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">:api</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:api_auth</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="20">
</div><div class="line" data-line="21">  <span style="color: #8cf8f7;">resources</span> <span style="color: #b3f6c0;">&quot;/users&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">UserController</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">only: </span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">:update</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:show</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:delete</span><span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="22"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>For those unfamiliar with <code>pipelines</code>, please reference the <a href="https://hexdocs.pm/phoenix/routing.html#pipelines">Phoenix guides</a>.</p>
<p>We define a new pipeline called <code>api_auth</code> for routes that require authorization, which in my case will be all routes except for <code>UserController#sign_in</code> and <code>UserController#create</code>.</p>
<p>Phoenix lets us define the same scopes multiple times without overwriting them. Here I define the <code>api</code> and <code>v1</code> scopes twice, piping the first only through the <code>api</code> pipeline and piping the second through the <code>api</code> <em><strong>and</strong></em> <code>api_auth</code> pipelines.</p>
<p>The <code>api_auth</code> pipeline consists of a custom plug I defined in <code>/lib/my_app_web/auth/auth_pipeline.ex</code>.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;"># /lib/my_app_web/auth/auth_pipeline.ex</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">MyAppWeb.Guardian.AuthPipeline</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">use</span> <span style="color: #e0e2ea;">Guardian.Plug.Pipeline</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">otp_app: </span><span style="color: #8cf8f7;">:my_app</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="5">                              <span style="color: #8cf8f7;">module: </span><span style="color: #e0e2ea;">MyAppWeb.Guardian</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="6">                              <span style="color: #8cf8f7;">error_handler: </span><span style="color: #e0e2ea;">MyAppWeb.Guardian.AuthErrorHandler</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8">  <span style="color: #8cf8f7;">plug</span> <span style="color: #e0e2ea;">Guardian.Plug.VerifyHeader</span>
</div><div class="line" data-line="9">  <span style="color: #8cf8f7;">plug</span> <span style="color: #e0e2ea;">Guardian.Plug.EnsureAuthenticated</span>
</div><div class="line" data-line="10"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>The first line of this plug defines some boiler plate that Guardian will use. The last two lines are what we want to check the authorization of the user. <code>VerifyHeader</code> will look for the the Authorization header in the request, which should contain <code>Bearer &lt;your token&gt;</code>, and <code>EnsureAuthenticated</code> will make sure that the token is valid.</p>
<p>In the first line of boiler plate, we defined an error handler, <code>MyAppWeb.Guardian.AuthErrorHandler</code>, mine consists of the example code from the Guardian documentation.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #9b9ea4;"># /lib/my_app_web/auth/auth_error_handler.ex</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">MyAppWeb.Guardian.AuthErrorHandler</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">import</span> <span style="color: #e0e2ea;">Plug.Conn</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">auth_error</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">type</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_reason</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_opts</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="7">    <span style="color: #e0e2ea;">body</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">Poison</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">encode!</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">message: </span><span style="color: #8cf8f7;">to_string</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">type</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="8">    <span style="color: #8cf8f7;">send_resp</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">401</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">body</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="9">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="10"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<h2><a href="#testing" aria-hidden="true" class="anchor" id="testing"></a>Testing</h2>
<p>At first I was unsure of how to test this functionality, but the solution turned out to be rather simple.</p>
<p>I've organized my test file to have two <code>describe</code> blocks, one for tests that require authentication and one for tests that don't require it. I have a top level <code>setup</code> block that adds the headers to the request that are common to all tests.</p>
<p>In the describe block for the tests requiring authentication, I wrote another <code>setup</code> block.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">setup</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">conn: </span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #9b9ea4;"># create a user</span>
</div><div class="line" data-line="3">  <span style="color: #e0e2ea;">user</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">insert</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">:user</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">email: </span><span style="color: #b3f6c0;">&quot;user@email.com&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">username: </span><span style="color: #b3f6c0;">&quot;user&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5">  <span style="color: #9b9ea4;"># create the token</span>
</div><div class="line" data-line="6">  <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">token</span><span style="color: #e0e2ea;">,</span> <span style="color: #9b9ea4;">_claims</span><span style="color: #e0e2ea;">&rbrace;</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">MyAppWeb.Guardian</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">encode_and_sign</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8">  <span style="color: #9b9ea4;"># add authorization header to request</span>
</div><div class="line" data-line="9">  <span style="color: #e0e2ea;">conn</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">conn</span> <span style="color: #e0e2ea;">|&gt;</span> <span style="color: #8cf8f7;">put_req_header</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;authorization&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;Bearer <span style="color: #8cf8f7;">#&lbrace;</span><span style="color: #e0e2ea;">token</span><span style="color: #8cf8f7;">&rbrace;</span>&quot;</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11">  <span style="color: #9b9ea4;"># pass the connection and the user to the test</span>
</div><div class="line" data-line="12">  <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">conn: </span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">user: </span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="13"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>Any requests you make in your tests should now have the appropriate headers!</p>
<ul>
<li>I am using the <a href="https://hexdocs.pm/ex_unit/ExUnit.html">ExUnit</a> testing framework (comes with Elixir).</li>
</ul>
<hr />
<p>If you found this helpful, please let me know! You can find me on twitter as <a href="https://twitter.com/mitchhanberg">@mitchhanberg</a> or you can shoot me an email.</p> ]]></description>
    </item>
    <item>
       <title>Scheduling Cron Jobs on Heroku with Ruby on Rails</title>
       <link>https://www.mitchellhanberg.com/post/2017/10/25/cron-jobs-on-heroku/</link>
       <pubDate>Wed, 25 Oct 2017 09:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2017/10/25/cron-jobs-on-heroku/</guid>
       <description><![CDATA[ <p>A common practice is to create a <strong>cron job</strong> whenever you have a task you need done periodically.</p>
<blockquote>
<p>The software utility <a href="https://en.wikipedia.org/wiki/Cron">Cron</a> is a time-based job scheduler in Unix-like computer operating systems. People who set up and maintain software environments use cron to schedule jobs (commands or shell scripts) to run periodically at fixed times, dates, or intervals. [^1]</p>
</blockquote>
<p>On something like AWS, you would have the ability to create normal cron jobs and, when dealing with a Ruby on Rails application, you could use the <a href="https://github.com/javan/whenever">Whenever</a> gem to interface between Cron and your application code.</p>
<p><strong>On Heroku, this isn't possible</strong></p>
<p>This is due to <a href="https://www.heroku.com/dynos">Heroku's Dyno</a> infrastructure, which is composed of virtualized Linux containers that are restarted daily, which resets it's file system. Heroku also states that <a href="https://adam.herokuapp.com/past/2010/4/13/rethinking_cron/">Cron does not perform well on horizontally scaling platforms</a>.</p>
<p>Luckily, Heroku has an add-on that just came out of beta which makes for a pretty easy solution. The <a href="https://elements.heroku.com/addons/scheduler">Scheduler</a> add-on is free, utilizes one-off dynos (whose uptime will count towards your monthly usage), and will make use of Rake tasks (for non-rails applications, scripts can be add to the <code>bin/</code> directory).</p>
<p>Let's install the add-on. This can be done via the CLI or the web UI, but I'll show you the CLI method. This assumes you already have the Heroku CLI installed and logged in.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">$</span> <span style="color: #e0e2ea;">heroku</span> <span style="color: #e0e2ea;">addons:create</span> <span style="color: #e0e2ea;">scheduler:standard</span>
</div></code></pre>
<p>Now let's define a task. Let's say you're app needs to send a reminder for customers with appointments that day.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-ruby" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #8cf8f7;">desc</span> <span style="color: #b3f6c0;">&quot;</span><span style="color: #b3f6c0;">Send appointment reminders</span><span style="color: #b3f6c0;">&quot;</span>
</div><div class="line" data-line="2"><span style="color: #8cf8f7;">task</span> <span style="color: #8cf8f7;">:send_reminders</span> <span style="color: #e0e2ea;">=&gt;</span> <span style="color: #8cf8f7;">:environment</span> <span style="color: #e0e2ea; font-weight: bold;">do</span> <span style="color: #9b9ea4;"># :environment will load our Rails app, so we can query the database with ActiveRecord</span>
</div><div class="line" data-line="3">  <span style="color: #e0e2ea;">appts</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">Appointments</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">where</span><span style="color: #e0e2ea;">(</span><span style="color: #8cf8f7;">appointment_date</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">Date</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">today</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5">  <span style="color: #e0e2ea;">appts</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">each</span> <span style="color: #e0e2ea; font-weight: bold;">do</span> <span style="color: #e0e2ea;">|</span><span style="color: #e0e2ea;">appt</span><span style="color: #e0e2ea;">|</span>
</div><div class="line" data-line="6">    <span style="color: #e0e2ea;">AppointmentMailer</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">reminder</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">appt</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="7">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="8"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>Now it's time to deploy this code.</p>
<p>Great, now how do we schedule this task to run? Let's hop over to Heroku's web UI. You can find this in the list of installed add-ons on the dashboard for your app, or you can run the following command in your terminal. (If you have more than one app, append <code>--app &lt;your app's name&gt;</code> to the command.)</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ heroku addons:open scheduler
</div></code></pre>
<p>You should now see a screen similar to this.</p>
<p><img src="/images/scheduler-add-on.jpg" alt="" /></p>
<p>Since we want to remind customers of their appointments for the day, we'll set the <strong>Frequency</strong> to <strong>Daily</strong> and the <strong>Next Due</strong> to <strong>10:00</strong>. Note that the <strong>Next Due</strong> time is in UTC, so <strong>10:00</strong> is 6:00AM where I live. After clicking <strong>Save</strong>, your scheduled job should be live.</p>
<p>And there we have it! This solution is ideal for basic tasks like sending emails, but for heavier, longer running jobs, you should look into configuring a <a href="https://devcenter.heroku.com/articles/scheduled-jobs-custom-clock-processes">Custom Clock process</a>.</p> ]]></description>
    </item>
    <item>
       <title>Encoding Ecto Validation Errors in Phoenix 1.3</title>
       <link>https://www.mitchellhanberg.com/post/2017/10/23/encoding-ecto-validation-errors-in-phoenix/</link>
       <pubDate>Mon, 23 Oct 2017 09:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2017/10/23/encoding-ecto-validation-errors-in-phoenix/</guid>
       <description><![CDATA[ <h2><a href="#problem" aria-hidden="true" class="anchor" id="problem"></a>Problem</h2>
<p>I recently ran into this error while implementing the first endpoint of my Phoenix JSON API.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">** (Poison.EncodeError) unable to encode value: &lbrace;:username, &lbrace;&quot;has already been taken&quot;, []&rbrace;&rbrace;
</div></code></pre>
<p>After a bit of googling and detective work, I found the offending piece of code, located in my <code>error_view.ex</code> file.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">render</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">&quot;409.json&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #8cf8f7;">changeset: </span><span style="color: #e0e2ea;">changeset</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="2">  <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="3">    <span style="color: #8cf8f7;">status: </span><span style="color: #b3f6c0;">&quot;failure&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="4">    <span style="color: #8cf8f7;">errors: </span><span style="color: #e0e2ea;">changeset</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">errors</span> <span style="color: #9b9ea4;"># this line causes the error</span>
</div><div class="line" data-line="5">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="6"><span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>This function handles rendering the JSON payload that the controller sends back to the client when there is an error.</p>
<p>The <code>errors</code> property of the <code>changeset</code> struct is a <a href="https://elixir-lang.org/getting-started/keywords-and-maps.html#keyword-lists">keyword list</a>* of <code>error</code>'s, with <code>error</code> being  a type defined in the <a href="https://github.com/elixir-ecto/ecto/blob/v2.2.6/lib/ecto/changeset.ex#L250">Changeset module</a>.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #8cf8f7;"><span style="color: #e0e2ea;">type <span style="color: #e0e2ea;">error</span> <span style="color: #e0e2ea;">::</span> <span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">String</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">t</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">Keyword</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">t</span><span style="color: #e0e2ea;">&rbrace;</span></span></span></span></span>
</div></code></pre>
<p><a href="https://github.com/devinus/poison">Poison</a> is not able to encode this, so a <code>Poison.EncodeError</code> error is raised.</p>
<p>* It's important to remember that a keyword list is a list of 2-item tuples with the first item of the tuple being an atom. So the error we originally saw was the key-value pair that couldn't be encoded, shown in tuple form.</p>
<hr />
<h2><a href="#solution" aria-hidden="true" class="anchor" id="solution"></a>Solution</h2>
<p>If you created your Phoenix app when Phoenix was at v1.3, then you should have this function in the <code>/lib/your_app_web/views/error_helpers.ex</code> file. If not, go ahead and paste it in that file.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;">@<span style="color: #9b9ea4;">doc</span> <span style="color: #b3f6c0;">&quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="2"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">  Translates an error message using gettext.</span></span></span>
</div><div class="line" data-line="3"><span style="color: #e0e2ea;"><span style="color: #9b9ea4;"><span style="color: #b3f6c0;">  &quot;&quot;&quot;</span></span></span>
</div><div class="line" data-line="4">  <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">translate_error</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&lbrace;</span><span style="color: #e0e2ea;">msg</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">opts</span><span style="color: #e0e2ea;">&rbrace;</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="5">    <span style="color: #9b9ea4;"># Because error messages were defined within Ecto, we must</span>
</div><div class="line" data-line="6">    <span style="color: #9b9ea4;"># call the Gettext module passing our Gettext backend. We</span>
</div><div class="line" data-line="7">    <span style="color: #9b9ea4;"># also use the &quot;errors&quot; domain as translations are placed</span>
</div><div class="line" data-line="8">    <span style="color: #9b9ea4;"># in the errors.po file.</span>
</div><div class="line" data-line="9">    <span style="color: #9b9ea4;"># Ecto will pass the :count keyword if the error message is</span>
</div><div class="line" data-line="10">    <span style="color: #9b9ea4;"># meant to be pluralized.</span>
</div><div class="line" data-line="11">    <span style="color: #9b9ea4;"># On your own code and templates, depending on whether you</span>
</div><div class="line" data-line="12">    <span style="color: #9b9ea4;"># need the message to be pluralized or not, this could be</span>
</div><div class="line" data-line="13">    <span style="color: #9b9ea4;"># written simply as:</span>
</div><div class="line" data-line="14">    <span style="color: #9b9ea4;">#</span>
</div><div class="line" data-line="15">    <span style="color: #9b9ea4;">#     dngettext &quot;errors&quot;, &quot;1 file&quot;, &quot;%&lbrace;count&rbrace; files&quot;, count</span>
</div><div class="line" data-line="16">    <span style="color: #9b9ea4;">#     dgettext &quot;errors&quot;, &quot;is invalid&quot;</span>
</div><div class="line" data-line="17">    <span style="color: #9b9ea4;">#</span>
</div><div class="line" data-line="18">    <span style="color: #e0e2ea; font-weight: bold;">if</span> <span style="color: #e0e2ea;">count</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">opts</span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">:count</span><span style="color: #e0e2ea;">]</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
</div><div class="line" data-line="19">      <span style="color: #e0e2ea;">Gettext</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">dngettext</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">ContactWeb.Gettext</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;errors&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">msg</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">msg</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">count</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">opts</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="20">    <span style="color: #e0e2ea; font-weight: bold;">else</span>
</div><div class="line" data-line="21">      <span style="color: #e0e2ea;">Gettext</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">dgettext</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">ContactWeb.Gettext</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">&quot;errors&quot;</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">msg</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">opts</span><span style="color: #e0e2ea;">)</span>
</div><div class="line" data-line="22">    <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div><div class="line" data-line="23">  <span style="color: #e0e2ea; font-weight: bold;">end</span>
</div></code></pre>
<p>And then we make the following change.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-diff" translate="no" tabindex="0"><div class="line" data-line="1">def render(&quot;409.json&quot;, %&lbrace;changeset: changeset&rbrace;) do
</div><div class="line" data-line="2">  %&lbrace;
</div><div class="line" data-line="3">    status: &quot;failure&quot;,
</div><div class="line" data-line="4"><span style="color: #ffc0b9;"><span style="color: #8cf8f7;">-</span>   errors: changeset.errors # this line causes the error</span>
</div><div class="line" data-line="5"><span style="color: #b3f6c0;"><span style="color: #8cf8f7;">+</span>   errors: Ecto.Changeset.traverse_errors(changeset, &amp;translate_error/1)</span>
</div><div class="line" data-line="6">  &rbrace;
</div><div class="line" data-line="7">end
</div></code></pre>
<p>Here we use the <a href="https://hexdocs.pm/ecto/Ecto.Changeset.html#traverse_errors/2">Ecto.Changeset.traverse_errors/2</a> function to apply the <code>translate_errors/1</code> function to each error, which will return a map that can then be encoded by Poison.</p>
<p>Here is the JSON that we can now render and send to the client!</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-json" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="2">  <span style="color: #a6dbff;">&quot;status&quot;</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">&quot;failure&quot;</span><span style="color: #e0e2ea;">,</span>
</div><div class="line" data-line="3">  <span style="color: #a6dbff;">&quot;errors&quot;</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">&lbrace;</span>
</div><div class="line" data-line="4">    <span style="color: #a6dbff;">&quot;email&quot;</span><span style="color: #e0e2ea;">:</span> <span style="color: #e0e2ea;">[</span>
</div><div class="line" data-line="5">        <span style="color: #b3f6c0;">&quot;has already been taken&quot;</span>
</div><div class="line" data-line="6">    <span style="color: #e0e2ea;">]</span>
</div><div class="line" data-line="7">  <span style="color: #e0e2ea;">&rbrace;</span>
</div><div class="line" data-line="8"><span style="color: #e0e2ea;">&rbrace;</span>
</div></code></pre>
<hr />
<p>If you found this helpful, please let me know! You can find me on twitter as <a href="https://twitter.com/mitchhanberg">@mitchhanberg</a> or you can shoot me an email.</p> ]]></description>
    </item>
    <item>
       <title>Installing Erlang and Elixir with asdf</title>
       <link>https://www.mitchellhanberg.com/post/2017/10/05/installing-erlang-and-elixir-using-asdf/</link>
       <pubDate>Thu, 05 Oct 2017 11:30:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2017/10/05/installing-erlang-and-elixir-using-asdf/</guid>
       <description><![CDATA[ <div class="markdown-alert markdown-alert-caution">
<p class="markdown-alert-title">Caution</p>
<p>ASDF has changed a lot since this was written, you'll want to consult the <a href="https://asdf-vm.com">documentation</a> first.</p>
</div>
<p><strong>Let's set the scene</strong></p>
<p><em>You've been fully entrenched in the Ruby and Rails world for 10 months</em></p>
<p><em>You've just tied a bow on your latest side project</em></p>
<p><em>You're already bored and excited to start something new</em></p>
<p><em>For the sake of this post, you work on a mac</em></p>
<p>So you've decided to mess around with Elixir! Nice choice, let's get started.</p>
<hr />
<h2><a href="#what-is-erlang" aria-hidden="true" class="anchor" id="what-is-erlang"></a>What is Erlang?</h2>
<blockquote>
<p>Erlang is a programming language used to build massively scalable soft real-time systems with requirements on high availability. Some of its uses are in telecoms, banking, e-commerce, computer telephony and instant messaging. Erlang's runtime system has built-in support for concurrency, distribution and fault tolerance.</p>
</blockquote>
<h2><a href="#what-is-elixir" aria-hidden="true" class="anchor" id="what-is-elixir"></a>What is Elixir?</h2>
<blockquote>
<p>Elixir is a dynamic, functional language designed for building scalable and maintainable applications.</p>
<p>Elixir leverages the Erlang VM, known for running low-latency, distributed and fault-tolerant systems, while also being successfully used in web development and the embedded software domain.</p>
</blockquote>
<p>Since you are coming from a Ruby background, you know how valuable using a version manager like <a href="https://github.com/rbenv/rbenv">rbenv</a> or <a href="https://github.com/rvm/rvm">rvm</a> can be. Since we are going to be installing two different programming language platforms, let's find a solution that can handle all of our needs.</p>
<h2><a href="#what-is-asdf" aria-hidden="true" class="anchor" id="what-is-asdf"></a>What is asdf?</h2>
<blockquote>
<p><em>extendable version manager</em></p>
<p>Supported languages include Ruby, Node.js, Elixir and more. Supporting a new language is as simple as this plugin API.</p>
</blockquote>
<p><a href="https://github.com/asdf-vm/asdf">asdf</a> allows us to use one tool to manage both Elixir and Erlang, and the next time I'm working on a Ruby project, I will probably switch over to using it as well.</p>
<p>We can easily install asdf using <a href="https://brew.sh/">Homebrew</a>.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ brew install asdf coreutils curl git
</div></code></pre>
<p>The next important bit is at the bottom of the output stating that you will need to initialize asdf whenever you start your shell session. I ran the following command to add that line to my .zshrc file, but you should add it to the approriate rc file for your shell.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ echo &#39;source $(brew --prefix asdf)/asdf.sh&#39; &gt;&gt; .zshrc
</div></code></pre>
<h2><a href="#installing-erlang-and-elixir" aria-hidden="true" class="anchor" id="installing-erlang-and-elixir"></a>Installing Erlang and Elixir</h2>
<p>We're almost there! Since asdf relies on language plugins, we have to add those now and any dependencies they might require.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ brew install autoconf
</div><div class="line" data-line="2">$ asdf plugin add erlang
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">$ brew install unzip
</div><div class="line" data-line="5">$ asdf plugin add elixir
</div></code></pre>
<p>Now we want to install the actual languages. The syntax for this is <code>asdf install &lt;language&gt; &lt;version&gt;</code>, so we can do the following.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ asdf install erlang 23.2.5
</div><div class="line" data-line="2">
</div><div class="line" data-line="3">$ asdf install elixir 1.11.3
</div></code></pre>
<p>If you want to see a list of available versions for each langauge, you can run the command <code>asdf list all &lt;language&gt;</code></p>
<p>Let's make sure that they were successfully installed, running the following commands should produce this output.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ source ~/.zshrc
</div><div class="line" data-line="2">
</div><div class="line" data-line="3">$ which erl
</div><div class="line" data-line="4">/Users/mitchell/.asdf/shims/erl
</div><div class="line" data-line="5">
</div><div class="line" data-line="6">$ which elixir
</div><div class="line" data-line="7">/Users/mitchell/.asdf/shims/elixir
</div></code></pre>
<p>asdf has the concepts of global versions and a local versions, but we are going to focus on the local version concept.</p>
<p>Once we navigate into the directory of our project, we can set the version of Erlang and Elixir that we want it to use.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ cd ~/my-elixir-project
</div><div class="line" data-line="2">$ asdf local erlang 23.2.5
</div><div class="line" data-line="3">$ asdf local elixir 1.11.3
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"># Set them globally
</div><div class="line" data-line="6">$ asdf global erlang 23.2.5
</div><div class="line" data-line="7">$ asdf global elixir 1.11.3
</div></code></pre>
<p>This will add to (or create if there isn't one already) the <code>.tools-version</code> file in the root of your project folder. It should now resemble this.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">erlang 23.2.5
</div><div class="line" data-line="2">elixir 1.11.3
</div></code></pre>
<p>Boom! You are now ready to get started with Elixir. Checking the version of Elixir should now confirm that your project can find and use it.</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">$ elixir --version
</div><div class="line" data-line="2">Erlang/OTP 23 [erts-11.1.7] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">Elixir 1.11.3 (compiled with Erlang/OTP 21)
</div></code></pre> ]]></description>
    </item>
    <item>
       <title>Habits of Successful Projects</title>
       <link>https://www.mitchellhanberg.com/post/2017/10/03/habits-of-successful-projects/</link>
       <pubDate>Tue, 03 Oct 2017 08:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2017/10/03/habits-of-successful-projects/</guid>
       <description><![CDATA[ <p>Over the course of a project, it is easy to become jaded and to lose track of why your team has certain processes. It's important to stay vigilant to keep those processes from degrading, because you will have trouble finding the cause of a reduction in velocity or quality due to the misunderstanding of how the team is operating.</p>
<p>This post details some habits that I've noticed to be invaluable.</p>
<h2><a href="#trying-something-new" aria-hidden="true" class="anchor" id="trying-something-new"></a>Trying something new</h2>
<p>Pick something and <em>execute</em>.</p>
<p>This is especially important when you are giving a new project strategy a trial run, because if you don't follow through then you have no idea if the strategy didn't work. You can't be iterative if you can't learn from your mistakes and you can't learn from your mistakes if you can't identify them.</p>
<p>Have a meeting and make some documents that detail the new protocol, so everyone should know what is expected and be able to comply.</p>
<h2><a href="#the-little-things-do-matter" aria-hidden="true" class="anchor" id="the-little-things-do-matter"></a>The little things do matter</h2>
<p>Focusing on rules can seem pedantic but setting expectations will create a consistent, trustworthy environment that optimizes for visibility. If your team is following the project's rules, you can be confident that you know the current state of the project. This passive communication can relieve a heavy mental load that comes with constantly wondering what progress the team is making.</p>
<p>If you find your team going through your project board during a stand-up/status meeting and teammates are saying, "Oh, I finished that a week ago" or "Oh, that's blocked on <em>thing</em>," you have a problem and need to address it.</p>
<h2><a href="#meetings" aria-hidden="true" class="anchor" id="meetings"></a>Meetings</h2>
<p>With the exception of a daily stand-up/status meeting, meetings should take the form of a group activity or should spin off activities. Things should either be <em>getting done</em> or be explicitly delegated to <em>get done</em>, the phrasing of the latter is important. This is not the same as saying, "We'll worry about this later."</p>
<p>Don't go down rabbit holes, so be comfortable steering a discussion back on track if it has veered off topic. The meeting is not the time to engineer a solution to the problem at hand, but to decide on the direction and determine who is best equipped to tackle it.</p>
<h2><a href="#retrospectives" aria-hidden="true" class="anchor" id="retrospectives"></a>Retrospectives</h2>
<p>Retros are the time to decide which practices to toss, which to keep, and on which to double down. Care is needed to prevent these meetings from turning into an hour-long venting session that doesn't offer any solutions.</p>
<p>During your sprint, write down topics that you'd like to discuss and put them in a centralized place so that others can see them. This gives your teammates time to ponder your topic, making their response more valuable than if you gave them 10 seconds to think it through.</p>
<hr />
<p>We love designing, building and deploying, so it's easy to get carried away and forgo process for the sake of getting things done. That's okay, but if you and your team have an "official" protocol you should follow it or exchange it for something that better suits your project's needs.</p> ]]></description>
    </item>
    <item>
       <title>First Dive into Development for VR</title>
       <link>https://www.mitchellhanberg.com/post/2017/04/11/vr-first-steps/</link>
       <pubDate>Tue, 11 Apr 2017 09:00:00 EDT</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2017/04/11/vr-first-steps/</guid>
       <description><![CDATA[ <h2><a href="#intro" aria-hidden="true" class="anchor" id="intro"></a>Intro</h2>
<p>Having some free time, I was asked to investigate the current state of Virtual Reality (hardware, development kits, etc) and to attempt build a prototype in order to start building our chops.</p>
<p>Going into this, I didn't have any prior experience with VR, 3D modeling, or physics engines and I wasn't sure if I would be able to even get anything to compile, yet I was able to build a small game and deploy it to a VR headset in only a few days.</p>
<h2><a href="#the-platforms" aria-hidden="true" class="anchor" id="the-platforms"></a>The Platforms</h2>
<p>There are a couple of categories: Desktop VR and Mobile VR.</p>
<p>Examples of desktop VR are the Oculus Rift and the HTC Vive. These devices are headsets that are connected to a PC primarily used to play video games that have been optimized for VR.</p>
<p>Examples of mobile VR are the Samsung Gear VR (which is powered by Oculus) and Google Daydream. Both of which can only run on certain devices.</p>
<p>For our situation, we already had a Gear VR compatible phone (Samsung Galaxy S7), so we went down that path.</p>
<h2><a href="#development" aria-hidden="true" class="anchor" id="development"></a>Development</h2>
<p>What I learned during this experiment is that building a "VR app" is really just creating something in a game engine, enabling VR support, and then building it for whatever platform. The VR layer of the whole process is rather thin, it's more about keeping VR in mind during development in order to really take advantage of the platform.</p>
<p>Most of the VR platforms have support for Unity and Unreal Engine, so I chose Unity and followed their basic <a href="https://unity3d.com/learn/tutorials/projects/roll-ball-tutorial">tutorial</a> (a roll-a-ball game, in which the player moves a ball to collect objects), just to get something up and running.</p>
<p>Unity has an amazing amount of documentation available, so getting it installed and commencing on the tutorial game was a fairly painless process. I was able to get the tutorial finished in a couple of hours.</p>
<p><img src="/images/roll-a-ball-screenshot.png" alt="picture" /></p>
<p>At this point it was time to get it on the phone and be able to view it in the headset. We hadn't bought the actual Gear VR headset yet, but we were able to find a 3rd party headset somewhere in the office.</p>
<p>The steps to deploy were pretty simple:</p>
<ol>
<li>Install the Android SDK</li>
<li>Point Unity to the path of the SDK</li>
<li>Create an Oculus .osig and copy it to the appropriate folder in your project</li>
<li>Configure the appropriate build settings</li>
<li>Hit "Build and Run"</li>
</ol>
<p>Normally the phone will have a "Insert into headset" message and won't display your app until it's inserted, but after enabling developer mode from within the "Gear VR Service", you are able to start the app and view it with a 3rd party headset.</p>
<p>The Gear VR headset has a touchpad on it, which allows you to interact with the app while viewing it with the headset. The headset I was using did not have such capabilities and I had yet to create a work around, so at this point you couldn't actually play the game while wearing the headset.</p>
<h2><a href="#retrospective" aria-hidden="true" class="anchor" id="retrospective"></a>Retrospective</h2>
<p>The key aspects of creating content for the Virtual Reality platform were not what I expected and it feels cool to play around with a game engine. The business opportunities for VR are still being uncovered and it'll be interesting to see how the platform evolves.</p> ]]></description>
    </item>
    <item>
       <title>Leaving Your Legacy</title>
       <link>https://www.mitchellhanberg.com/post/2017/03/09/leaving-your-legacy/</link>
       <pubDate>Thu, 09 Mar 2017 09:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2017/03/09/leaving-your-legacy/</guid>
       <description><![CDATA[ <h2><a href="#introduction" aria-hidden="true" class="anchor" id="introduction"></a>Introduction</h2>
<p>At one point or another, we all will work on a legacy project. You're between projects and you get put on an internal effort, you're starting a new project with a client and inherit the old codebase, or something or another.</p>
<p>I was fortunate enough to get placed directly on a project when I joined my company, rich with modern coding practices. Code review, scrum-style backlog and story mapping, and a team full of seasoned engineers who were all familiar with the codebase.</p>
<p>Until it was my turn to say goodbye to the team and hello to new opportunities, and in the meantime, I would concentrate my efforts on one of our internal projects.</p>
<p>This post serves as an outlet for my experience.</p>
<div class="markdown-alert markdown-alert-important">
<p class="markdown-alert-title">Important</p>
<p>Team size = me.</p>
</div>
<h2><a href="#the-stack" aria-hidden="true" class="anchor" id="the-stack"></a>The Stack</h2>
<p>Before starting on this project, I had an interest in exploring a new stack of technology, particularly Ruby web development with the Ruby on Rails framework. I had previously worked heavily in the .NET web stack and wanted to try my hand at something different.</p>
<p>And then just like that, I'm told I have the opportunity to work on a Rails project in a professional setting, woo! I'm excited.</p>
<p>Working through my first task, naturally, I do some googling. I notice that everything I'm seeing online looks totally different than what I'm seeing in the codebase. Eventually, I come to the realization that I'm working on Rails 3.2 and Ruby 1.9, a 5-year-old framework and a 10-year-old language. All I know about Ruby at this point has been deprecated for years (to a degree, the general syntax is similar).</p>
<p>Learning the ins and outs of an old version of a tool just seems backward, especially if you haven't used that tool before. Is upgrading to the latest versions worth it for this project? Not really, but that seems to be part of the problem.</p>
<h2><a href="#testing" aria-hidden="true" class="anchor" id="testing"></a>Testing</h2>
<p>When I first started exploring the codebase, I was excited to see how one went about testing in Ruby/Rails, only to find that half of the tests were failing.</p>
<p>I spent a week ironing out the tests. With test failures ranging from a lack of test fixtures to commented out blocks of deprecated code from a framework upgrade that never got finished.</p>
<p>Testing stops being useful when you can't trust your tests.</p>
<p>This is where the difficulties of working on an internal project start to crop up, you see all these broken windows, but don't have the drive to fix them. You become desensitized to the lower quality of code and the codebase suffers for it.</p>
<p>I fixed all the tests, but at this point, I ask myself, are these tests even valuable? What are they testing?</p>
<h2><a href="#deployment" aria-hidden="true" class="anchor" id="deployment"></a>Deployment</h2>
<blockquote>
<p>Previously there was Jenkins integration with building and deployment using CloudFoundry, but this was not maintained and no longer works. Therefore, all staging and production deployments must be done manually</p>
</blockquote>
<p>This was an actual quote from the project's wiki page (until I deleted it). I wasn't surprised by a defunct continuous integration setup, but that the expected deployment process was to <em>manually copy the files</em>.</p>
<p>I did not think that this project needed some fancy system for deployment, but that just seemed wrong.</p>
<p>I then experimented with just replacing the code on the server with a clone of the repository, and it deploy all it required was a git pull. Not much effort was put into this, but it was simple to implement and simple to use.</p>
<h2><a href="#decay" aria-hidden="true" class="anchor" id="decay"></a>Decay</h2>
<p>This is the general theme, each time a new developer joins the project temporarily they will leave behind something corrosive, slowly decaying the project. Even experienced engineers can write some bad code if the circumstances are begging for it.</p>
<p>Out-of-date comments are left to rot, wikis are stale and inaccurate, and tests break and remain broken. Now this happens over time, and I'm not sure if you'd be able to recognize it happening in real time.</p>
<p>In order to stop (or at least slow down) the decay is to maintain verbose documentation. Commit messages need to be wordy and the wiki (if you have one) needs to be awfully explicit. Now I don't think it needs to be written for someone who isn't a developer, but it at least should be written for someone who has no experience in any of the project stack.</p>
<p>Example of a commit message that is not helpful</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">fixed brokn thing .
</div></code></pre>
<p>Example of a commit message that is helpful</p>
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">Fixed broken pagination after filtering list
</div><div class="line" data-line="2"> * Code was attempting to filter during pagination, now 
</div><div class="line" data-line="3">   the list filters completely and then paginates the filtered list
</div><div class="line" data-line="4"> * Updated the gem used for pagination from v0.5.1 to v1.0.1
</div></code></pre>
<p>I even think a project journal would be beneficial. After each task is finished, the developer will log everything that went into the task, including reasoning, failed attempts and possibly even short explanation of how a certain library was utilized. Anything that isn't written down is essentially lost for eternity, so any epiphanies you had are useless if you don't write them down for the next developer down the line.</p>
<h2><a href="#conclusion" aria-hidden="true" class="anchor" id="conclusion"></a>Conclusion</h2>
<p>Working with legacy code is inevitable, but with a little TLC, you can make it less painful for those who will work with it after you.</p>
<p>A couple takeaways from this experience are</p>
<ul>
<li>
<p>Always leave the project (or code, documentation, etc.) in a better condition than when you joined. Take the bit of extra time to jot down broken windows you see or valuable (yet not expensive) improvements you can make, to either fix after your task or during.</p>
</li>
<li>
<p>Find yourself a healthy level of criticality, don't assume all the code is crap, but don't assume that it's all great code.</p>
</li>
<li>
<p>Ask questions. If you have to track down someone who worked on the project 4 years ago, do it. You're not going to find their rationale for a design decision on Stack Overflow.</p>
</li>
</ul> ]]></description>
    </item>
    <item>
       <title>If You Don&#39;t Have Anything Valuable To Say... Say It Anyways</title>
       <link>https://www.mitchellhanberg.com/post/2017/02/28/first-post/</link>
       <pubDate>Tue, 28 Feb 2017 09:00:00 EST</pubDate>
       <guid>https://www.mitchellhanberg.com/post/2017/02/28/first-post/</guid>
       <description><![CDATA[ <h2><a href="#what-does-this-mean" aria-hidden="true" class="anchor" id="what-does-this-mean"></a>What does this mean?</h2>
<p>In the professional world, feedback is incredibly important and can vastly beneficial, but sometimes our fear of criticism can stop you from sharing, which then inhibits you from learning.</p>
<p>There's nothing I hate more than being wrong, more so than others knowing I was wrong. Being able to leverage others knowledge to accelerate your own growth is a key to success (in my opinion, this is the part where you can tell me I'm wrong).</p>
<p>Obviously the title of the post is insinuating that you should make a blog and write articles about the stuff you are up to or any remarkable experiences, but I think it also applies to other areas of your life. It's not just about saying or writing, but it's also about <strong>doing</strong>, and this is something with which I struggle.</p>
<h2><a href="#youll-never-finish-if-you-dont-start" aria-hidden="true" class="anchor" id="youll-never-finish-if-you-dont-start"></a>You'll never finish if you don't start</h2>
<p>Many people never start a project because they don't want to get it wrong; they're afraid to build something that doesn't work because it'll mean that they're dumb.</p>
<blockquote>
<p>Wrong</p>
</blockquote>
<p>Having produced half of a project is still better than having not started one at all. The experience you gained is more important than the finished product (not saying that finishing something isn't worth it, it definitely is).</p>
<p>The likelihood that anything revolutionary will come from this blog is low, but that's not going to stop me and it shouldn't stop you. Get out there and start that project, read that book, or just <strong>do</strong> that thing that you've been putting off.</p> ]]></description>
    </item>
  </channel>
</rss>
