<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jonathan Apodaca</title>
    <description>The latest articles on DEV Community by Jonathan Apodaca (@jrop).</description>
    <link>https://dev.to/jrop</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F29661%2Fbba128e1-d887-4b84-a695-dcc329280cb4.jpg</url>
      <title>DEV Community: Jonathan Apodaca</title>
      <link>https://dev.to/jrop</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jrop"/>
    <language>en</language>
    <item>
      <title>Exposing Services from Your Devices and Making Them Accessible from Anywhere Using SSH</title>
      <dc:creator>Jonathan Apodaca</dc:creator>
      <pubDate>Sun, 28 Feb 2021 05:32:31 +0000</pubDate>
      <link>https://dev.to/jrop/exposing-services-from-your-devices-and-making-them-accessible-from-anywhere-using-ssh-30on</link>
      <guid>https://dev.to/jrop/exposing-services-from-your-devices-and-making-them-accessible-from-anywhere-using-ssh-30on</guid>
      <description>&lt;h1&gt;
  
  
  Introduction and Goal
&lt;/h1&gt;

&lt;p&gt;There are many use cases where you want to forward traffic from a Gateway server to some other computer.  Perhaps one of the most famous examples of this are developer tools that "publish" local ports to publicly accessible endpoints (viz. Ngrok, localtunnel, etc). What many forget to mention, and thus what took me an embarrassingly long amount to time to rediscover, is that, for certain use cases, there already exists a tool built-in to UNIX that can do this out of the box (with one small configuration change on the Gateway server).&lt;/p&gt;

&lt;h1&gt;
  
  
  SSH: Background
&lt;/h1&gt;

&lt;p&gt;While SSH is generally thought of as a way to log-in to a remote server and get a shell to execute commands, it also supports many, many more less-mentioned features. The one we are most interested in in the context of this post is &lt;em&gt;port forwarding&lt;/em&gt;.  SSH supports two modes of port forwarding: local (controlled with the &lt;code&gt;-L&lt;/code&gt; switch), and remote (&lt;code&gt;-R&lt;/code&gt;). Which one you use depends on which way you want traffic to flow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;local: forwards local traffic to the remote host over the SSH tunnel&lt;/li&gt;
&lt;li&gt;remote: forwards remote traffic to the local host over the SSH tunnel&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Local Port-Forwarding
&lt;/h2&gt;

&lt;p&gt;In a contrived scenario where your friend has given you SSH access to a server on their LAN, and you want to log into their routers admin page, you can execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh -L 8081:192.168.1.1:80 theserveryouhaveaccessto.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and then you can open a browser on your local machine, point it at &lt;a href="http://localhost:8081"&gt;http://localhost:8081&lt;/a&gt;, and all traffic will be piped through the SSH tunnel to their router's admin page. (Hopefully your friend has changed the default password to their router to prevent any nefarious subsequent action...)&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote port-forwarding.
&lt;/h2&gt;

&lt;p&gt;Now let's make traffic flow in the opposite direction: let's say we have another contrived scenario where you are developing a website, and you access it at the local port &lt;code&gt;3000&lt;/code&gt; (&lt;a href="http://localhost:3000"&gt;http://localhost:3000&lt;/a&gt;). Now say you want to show your same friend how the website is looking in your current stage of development.  You have a problem: how do you "publish" your local port to a public place? Well, in this case we can execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh -R 3000:127.0.0.1:3000 theserveryouhaveaccessto.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now tell your friend to type this into the address bar of their preferred browser: &lt;a href="http://theserveryouhaveaccessto.com:3000"&gt;http://theserveryouhaveaccessto.com:3000&lt;/a&gt;.  Voiala!  They will be able to access the web-server running on your local machine.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setting up the Server
&lt;/h1&gt;

&lt;p&gt;Since remote port forwarding is (potentially) undesired default behavior, you &lt;em&gt;do&lt;/em&gt; have to make a configuration change to support it on the SSH server. Make sure you have this option enabled in &lt;code&gt;/etc/sshd/config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GatewayPorts yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart &lt;code&gt;sshd&lt;/code&gt;, and you're all set!&lt;/p&gt;

&lt;p&gt;Now, all of this is well and good, except that it is a manual process to setup forwarded ports, and following a reboot of your computer, you have to remember a lengthy command to run in order to publish ports on your gateway. To complicate matters further, if your network connection between your machine and the gateway gets interrupted, you have to restart the command manually.  Suppose you want to automate this process and remove the manual steps: how would one accomplish such a task?&lt;/p&gt;

&lt;p&gt;I'm glad you asked...&lt;/p&gt;

&lt;h2&gt;
  
  
  AutoSSH
&lt;/h2&gt;

&lt;p&gt;From autossh's man page:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;autossh is a program to start a copy of ssh and monitor it, restarting it as necessary should it die or stop passing traffic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This sounds perfect: it is exactly what we were looking for.  In order to automatically start publishing ports to a gateway involves basically replacing &lt;code&gt;ssh&lt;/code&gt; with &lt;code&gt;autossh&lt;/code&gt;, plus a few extra switches.&lt;/p&gt;

&lt;p&gt;Here is an example that I found to be "low-maintenance", yet may have debatably insecure practices. Use at your own risk.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;autossh -N -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -R 22223:localhost:22 theserverihaveaccessto.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now all we have to do is get the command to run whenever the system powers on.  There are a few ways to do this, depending on what OS you are running...&lt;/p&gt;

&lt;h2&gt;
  
  
  Linux: &lt;code&gt;systemd&lt;/code&gt; Sample Unit File
&lt;/h2&gt;

&lt;p&gt;If you are running systemd (Linux), this is the section that applies to you.&lt;/p&gt;

&lt;p&gt;Put this in &lt;code&gt;/etc/systemd/system/my-ssh-tunnel.service&lt;/code&gt; and enable with &lt;code&gt;systemctl enable my-ssh-tunnel.service&lt;/code&gt;. Don't forget to start it after enabling it (&lt;code&gt;systemctl start my-ssh-tunnel.service&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=my-ssh-tunnel
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=YOUR_USER_NAME_HERE
PIDFile=/tmp/my-ssh-tunnel.pid
Environment=AUTOSSH_PIDFILE=/tmp/my-ssh-tunnel.pid
Environment=AUTOSSH_DEBUG=1
ExecStart=/usr/bin/autossh -N -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -R 22223:localhost:22 theserverihaveaccessto.com
ExecStop=/usr/bin/pkill -F /tmp/my-ssh-tunnel.pid
Restart=always
RestartSec=60

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  MacOS: &lt;code&gt;launchd&lt;/code&gt; Sample &lt;code&gt;plist&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;MacOS does not come with systemd, but rather its own process manager called &lt;a href="https://www.launchd.info/"&gt;launchd&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- put in /Library/LaunchDaemons/my-ssh-tunnel.plist --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- to enable it: launchctl load /Library/LaunchDaemons/my-ssh-tunnel.plist --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;plist&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;my-ssh-tunnel&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;UserName&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;YOUR_USER_NAME_HERE&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;KeepAlive&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ThrottleInterval&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;30&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/usr/local/bin/autossh&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;-N&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;-o&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;UserKnownHostsFile=/dev/null&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;-o&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;StrictHostKeyChecking=no&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;-R&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;22222:localhost:22&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;theserverihaveaccessto.com&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Drawbacks
&lt;/h1&gt;

&lt;p&gt;SSH is an extremely versatile tool, that supports more features than meets the eye upon first inspection.  For a short time, I used the methodology described in this post to create my own poor-man's VPN: I exposed ports from several of my personal computers on a gateway so that I could access them from anywhere.&lt;/p&gt;

&lt;p&gt;The major downside to this approach is that once a port is exposed on a gateway, anybody can access the service on that port.  Please utilize this approach judiciously.&lt;/p&gt;

&lt;h1&gt;
  
  
  Further Research
&lt;/h1&gt;

&lt;p&gt;In an upcoming post I will discuss how to easily set up a VPN amongst your personal devices so that you can access them from anywhere, without any tricky port mapping!&lt;/p&gt;

</description>
      <category>ssh</category>
      <category>networking</category>
      <category>expose</category>
      <category>home</category>
    </item>
    <item>
      <title>Things I Use</title>
      <dc:creator>Jonathan Apodaca</dc:creator>
      <pubDate>Tue, 28 Jul 2020 20:49:19 +0000</pubDate>
      <link>https://dev.to/jrop/things-i-use-1860</link>
      <guid>https://dev.to/jrop/things-i-use-1860</guid>
      <description>&lt;p&gt;If you have 3 developers in a room, and you ask them which tools they prefer, you will inevitably get at least 4 different opinions.  One or two of them may vehemently defend one camp or another, while the others might hand-wave while muttering something about "the best tool for the job".  Still another may rise on a pedestal and start preaching about Emacs.&lt;/p&gt;

&lt;p&gt;Such is the landscape when it comes to the tools we all as developers use.  Today, you get to hear my voice of opinion regarding the things I have come to rely on for my day-to-day work.  As this will be a rather lengthy post, let's get right into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Goals
&lt;/h2&gt;

&lt;p&gt;When choosing tooling and utilities, my goal is to make development as efficient as possible.  In the past, I had other goals as well, such as building a system that supports music production: this is still important to me, but it is no longer something I spend a lot of time on, due to various reasons.  In the end, then, I want to be able to "boot to development" as fast as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operating Systems
&lt;/h2&gt;

&lt;p&gt;At the very moment when a developer sits down at their desk and starts their computer, lines have already been drawn. A side has already been chosen: what is your Operating System of preference?  Speaking in very general terms, you can pretty much loosely divide Operating Systems into two families: UNIX, and Windows.  The UNIX family of Operating Systems can be further divided into commercial variants (e.g., macOS, among others), and non-commercial variants.&lt;/p&gt;

&lt;p&gt;In the past, I have done development work on Windows, but nowadays I prefer to develop exclusively on UNIX OSes.  They say that "UNIX is an IDE", and that is definitely true.  I typically have many dependencies on several tools to create an IDE experience on UNIX.  Here are the different flavors I have come to enjoy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;li&gt;Ubuntu Linux&lt;/li&gt;
&lt;li&gt;Android&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When it comes to an all-in-one, polished package, macOS is a very nice UNIX-variant.  I thoroughly enjoy working on macOS, and that is my requested preference when working at a company if they are willing to provide a Mac as a workstation.  However, I am unwilling to purchase Macs for my personal use, as I believe that their hardware is overpriced.  Enter the other distributions that I listed above.  At home, I run Ubuntu Linux on my Desktop (and yes, I do dual-boot Windows so that I can run certain programs).  I have tried Arch in the past, but have come to realize that I prefer Debian-based Linux distros.&lt;/p&gt;

&lt;p&gt;When on-the-go, I use Android.  See the "mobile" section for more information on my setup there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terminal
&lt;/h2&gt;

&lt;p&gt;For me, most of my interaction with my computer happens in the Terminal, and therefore I spend some time setting it up in ways that make my time spent there easier.  Lately, my terminal emulator of choice has been &lt;a href="https://sw.kovidgoyal.net/kitty/index.html"&gt;kitty&lt;/a&gt;.  Kitty has all of the features that I want in a terminal emulator: it is fast, can render ligatures, and is configurable via dotfiles.  The close runner-up to Kitty is Alacritty, which can do all of that, except that, as of the time of this writing, it &lt;a href="https://github.com/alacritty/alacritty/issues/50"&gt;cannot render font ligatures (yet)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I wish I had time to write about each of these projects individually, but in the interest of time, I will leave a list here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://sw.kovidgoyal.net/kitty/"&gt;KiTTY&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tmux/tmux/wiki"&gt;tmux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fishshell.com/"&gt;Fish&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gitlab.com/jrop/dotfiles"&gt;Dotfiles&lt;/a&gt; - &lt;a href="https://yadm.io/#"&gt;YADM&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ryanoasis/nerd-fonts"&gt;JetBrains Mono Nerd Font&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/junegunn/fzf"&gt;FZF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stedolan.github.io/jq/"&gt;jq&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jesseduffield/lazygit"&gt;lazygit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/FiloSottile/mkcert"&gt;mkcert&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rclone.org/"&gt;rclone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://supervisord.org/"&gt;supervisor&lt;/a&gt; - when developing a microservice-architecture locally, this tool is truely invaluable&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/procps-ng/procps#tab-readme"&gt;watch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Text Editors
&lt;/h2&gt;

&lt;p&gt;My text-editing journey has endured many changes, and over the years, I have tried pretty much everything out.  The only editor I haven't fully dived into would be Emacs.  Up until about a year ago, I was an avid fan of VSCode, for it blends the best of many editors.  Having a built-in terminal, there was little not to love.  I kept, however, hearing the tales of the absurd efficiency of VI, but could not relate to it, mostly due to an unguided learning experience with it that over 10 years ago at my university.  I had forced myself to code a C++ project in ViM, and I learned enough to move around and get the project done.  However, what I did not know was that I was only utilizing around 5% of the power of ViM.&lt;/p&gt;

&lt;p&gt;This all changed recently when I bought a ViM course during one of Udemy's flash-sales.  I was quickly sold when I realized that I had never used text objects beyond the verb "word" and that I could couple them with visual selection, among many other ViM actions.  Today, I probably still utilize a mere fraction of ViM's power, but I am nonetheless much happier with the efficiency of text-editing in ViM on a day-to-day basis.&lt;/p&gt;

&lt;p&gt;I have chosen &lt;a href="https://neovim.io/"&gt;NeoVim&lt;/a&gt; as the particular flavor that I prefer to use if not for the simple fact that the devs of NeoVim are attempting to bring ViM into the 21st century.  I consider the following features to be game-changers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built-in LSP client (coming soon)&lt;/li&gt;
&lt;li&gt;Scriptable with Lua - I believe VimL is an extremely archane language that is showing its age.  While NeoVim will always have support for VimL, I believe Lua is the future to scripting ViM.&lt;/li&gt;
&lt;li&gt;...and many more features!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  macOS-specific utilities
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://brew.sh/"&gt;Homebrew&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/TermiT/Flycut"&gt;Flycut&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://karabiner-elements.pqrs.org/"&gt;Karabiner Elements&lt;/a&gt; - needed by Capslock&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Vonng/Capslock"&gt;Capslock&lt;/a&gt; - Make Capslock Great Again! &lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rectangleapp.com/"&gt;Rectangle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://espanso.org/"&gt;Espanso&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Languages
&lt;/h2&gt;

&lt;p&gt;Practically speaking, there are many ways a developer may dictate their intent to the computer being controlled.  Over the years, many different languages have arisen with different paradigms that all accomplish different goals in slightly different manners.  Here is a short list of languages and tools I reach for when solving a new problem.  Although I am capable of using other languages and tools, these are the ones I utilize if the choice is mine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Config/Data/Declarative

&lt;ul&gt;
&lt;li&gt;YAML&lt;/li&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;li&gt;TOML&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Shell

&lt;ul&gt;
&lt;li&gt;Fish&lt;/li&gt;
&lt;li&gt;Bash&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Scripting

&lt;ul&gt;
&lt;li&gt;JavaScript/TypeScript/NodeJS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;UI - Web technologies + JSX&lt;/li&gt;
&lt;li&gt;Rust&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Miscellaneous Productivity
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/slackhq/nebula"&gt;Nebula&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nomachine.com/"&gt;NoMachine&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Mobile
&lt;/h2&gt;

&lt;p&gt;On the go, it is convenient to be able to develop if I am in a pinch.  I do not primarily develop on mobile, but there are tools I use to make it possible if an emergency arises.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Android&lt;/li&gt;
&lt;li&gt;&lt;a href="https://termux.com/"&gt;Termux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Gboard&lt;/li&gt;
&lt;li&gt;&lt;a href="https://play.google.com/store/apps/details?id=org.pocketworkstation.pckeyboard"&gt;Hacker Keyboard (by Kaus Weidner)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Hardware
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;PC: 6-core...&lt;/li&gt;
&lt;li&gt;Laptop: MacBook Pro&lt;/li&gt;
&lt;li&gt;Keyboard: 65% Drop Alt (formerly an Anne Pro II)&lt;/li&gt;
&lt;li&gt;Mouse: Logitech MX Master 2S&lt;/li&gt;
&lt;li&gt;Headphones: Panasonic...&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Things I have my eye on
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Arch Linux&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Embarking on the Quest of Building Your Own Mechanical Keyboard</title>
      <dc:creator>Jonathan Apodaca</dc:creator>
      <pubDate>Sat, 23 May 2020 05:19:34 +0000</pubDate>
      <link>https://dev.to/jrop/embarking-on-the-quest-of-building-your-own-mechanical-keyboard-34p2</link>
      <guid>https://dev.to/jrop/embarking-on-the-quest-of-building-your-own-mechanical-keyboard-34p2</guid>
      <description>&lt;p&gt;For way too long into my development career, I gave a disgraceful little thought to the equipment I used. No exception to this rule was paid to how I viewed and selected the keyboard that I would use as my main utensil. Recently, however, through a fortuitous chain of events, my path would finally cross with the world of mechanical keyboards, and I am forever ruined from using an OEM-keyboard as my daily driver.&lt;/p&gt;

&lt;p&gt;Don't get me wrong: this journey is fraught with its own downsides. For one, the cost is significant.  While one may hop on over to Best Buy and pick up a keyboard for the price of a decent 6-pack, one would be hard-pressed to get a good mechanical keyboard for under $50. Let alone when one begins to go down the road of the different customizations that can be applied to their existing hardware! Neglect to pay attention, and your pocket-book will be $300 lighter. However, for the patient, a great benefit waits at the end, and once greeted by its rewards, nearly impossible it becomes to return to former habits. &lt;/p&gt;

&lt;h1&gt;
  
  
  Why?
&lt;/h1&gt;

&lt;p&gt;I was first introduced to the world of "superior" keyboards when I ran across a side-gig of Jeff Atwood's[1]: The &lt;a href="https://codekeyboards.com/"&gt;Code Keyboard&lt;/a&gt;. The thought of a keyboard that was dedicated to the situation of my craft piqued my imagination.  What then could be so enticing as to transcend the membrane keyboard I currently typed on at the time?  I didn't previously &lt;em&gt;know&lt;/em&gt; there were different &lt;em&gt;types&lt;/em&gt; of switches for keyboards. Why?  I suppose I just never really thought about it.  I began reading that one could even customize their experience by choosing a specific switch, of which there existed at &lt;em&gt;least&lt;/em&gt; 5 different choices[2].&lt;/p&gt;

&lt;p&gt;I was just about sold, and proceeded to check out the store, that is until I saw the price-tag.  At the time, $120 seemed a high premium for such a "frivolous" accessory. Then it seemed little more than a luxury. A mechanical keyboard could &lt;em&gt;definitely&lt;/em&gt; not be a necessity.&lt;/p&gt;

&lt;p&gt;Thought was hastily demoted to Aspiration: to be investigated at a later date, say, perhaps when the pocket-book was more willing to rise to the occasion.&lt;/p&gt;

&lt;p&gt;Enter the 2018 MacBook Pro, which my boss bought me when I started a new job. Now there was a premium machine, in all her 6-core beastly speed.  The honeymoon period was abruptly over, however, when the keyboard began to malfunction, and my text was increasingly littered with extraneous i's and u's. After a full day of coding, annoyance gave full birth to furry. Even that open-source debounce utility could not catch &lt;em&gt;all&lt;/em&gt; of the double-key-presses.  Now, Apple had issued a recall on the keyboard, but time could not be afforded. The machine would need to be admitted for a minimum of 3-days before the situation could be righted, and even at that, the procedure was a replacement &lt;em&gt;with the same parts&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There were no two ways about it.  Something had to be done. Amazon was cordially consulted, and her benevolence suggested a wireless solution. Something low-profile, say. Perhaps it would even fit atop the laptop's face-plate. Also, here is the perfect excuse you have been looking for to try out a mechanical keyboard.&lt;/p&gt;

&lt;p&gt;Sold. It will be here in two days.&lt;/p&gt;

&lt;p&gt;It was a dream for all of five minutes until I realized that whoever laid out the keyboard had placed the "?" to the right of the shift key. That purchase was quickly returned in favor of slightly more research.&lt;/p&gt;

&lt;p&gt;I did finally end up with a keyboard that I liked. However, my sights were set high, and my new Aspiration was to build a fully custom mechanical keyboard.&lt;/p&gt;

&lt;h1&gt;
  
  
  Parameters - Many Dimensions
&lt;/h1&gt;

&lt;p&gt;Alright, let's get down to the nitty-gritty details: just what &lt;em&gt;are&lt;/em&gt; some of the benefits of mechanical keyboards? Let us reduce the whole world into a few attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Size&lt;/li&gt;
&lt;li&gt;Switches&lt;/li&gt;
&lt;li&gt;Layout&lt;/li&gt;
&lt;li&gt;Programmability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Keyboard Size
&lt;/h2&gt;

&lt;p&gt;Keyboards come in &lt;em&gt;many&lt;/em&gt; different sizes to accommodate many different use-cases, and they range from &lt;em&gt;very&lt;/em&gt; small, to quite large!  Sizes are typically rated in %-ages, e.g., "100%" / "65%" / "60%" / "40%" / etc. Loosely the percentage refers to how many keys are on the board. If you want to see some really tiny keyboards, just plug "40% keyboard" into Google, and enjoy the gallery.&lt;/p&gt;

&lt;p&gt;The size that I prefer (for the moment) is 60%.  This size seems to strike a good balance in that it is big enough that most keys are available without hitting too many "Fn" keys, and yet it is small enough that most keys are within reach without stretching my fingers &lt;em&gt;too&lt;/em&gt; far.  It also fits nicely in my backpack, atop my laptop, or cleanly on my desk leaving ample space for whatever else might live on my desk-related workspace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Switches
&lt;/h2&gt;

&lt;p&gt;Now here is where the customization begins to get fun. There being ample reading material on this topic already available, I will provide a short synopsis of the different types of switches here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Linear&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
An even amount of pressure is required to actuate the key. Typically preferred by gamers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Tactile&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
The key travels down, and activation coincides with a tactile "bump" the typist can "feel". This bump serves to involve the senses when typing quickly by providing queues to your brain about when keys have activated. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Clicky&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Similar to tactile switches, a bump is present during actuation, along with an audible "click". This serves to involve not only the touch-sensation but also the typist's audio-sensory system to know precisely when keys have activated.&lt;/p&gt;

&lt;p&gt;There are many, &lt;a href="https://www.mechanicalkeyboards.com/switches/"&gt;&lt;em&gt;many&lt;/em&gt;&lt;/a&gt; different switches in each category, and there are even more categories than those presented here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layout
&lt;/h2&gt;

&lt;p&gt;Mechanical layout is to be differentiated from "key mapping". The mechanical layout is the layout your &lt;em&gt;eyes see&lt;/em&gt; when looking at the keyboard.  "The rightmost key on the middle row is about so-wide". "There are keys in all four corners". That kind of stuff. That being said, withing a generalized layout, you are pretty much free to customize it as you see fit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;ANSI&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
This is the traditional layout you will find on most keyboards-- at least in the USA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;ISO&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
This layout has a double-row enter key, along with other differences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;JIS&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Japanese layout&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;HHKB&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
This layout has fewer keys on the bottom row.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Ortholinear&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Have you ever noticed that on pretty much every keyboard, the rows are staggered?  Well, on an ortholinear keyboard, the keys are arranged in a grid.  This has benefits like making many keys easier to reach, but you may need to re-train your brain to adjust!&lt;/p&gt;

&lt;h2&gt;
  
  
  Programmability
&lt;/h2&gt;

&lt;p&gt;The programmability of custom keyboards is one of my favorite attributes. I like to make better use of my CapsLock key, but going about that through the usual OS-provided channels can be rather difficult on some operating systems. If the keyboard firmware itself lets you remap keys, then this is a cinch! Also, it's portable: I can plug my programmed keyboard into my Mac laptop, my Windows desktop, or my Linux desktop, and things &lt;em&gt;just work&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Some keyboards have modes you can enter (via nefarious key combinations) that let you remap keys from the keyboard itself, others provide proprietary software, but by-and-large, my favorite keyboards are those that operate off of the open-source QMK firmware. The downside to QMK is that you &lt;em&gt;may&lt;/em&gt; have to drop to a terminal compile your firmware when you want to remap keys. There is an online "configurator" that you can use, but QMK is definitely the least user-friendly of all these options.  However, given its power, it is nonetheless my favorite option.&lt;/p&gt;

&lt;p&gt;Also, if editing your keyboards C-code firmware sounds exciting, QMK offers that possibility to you as well.  This is just &lt;em&gt;really&lt;/em&gt; cool!&lt;/p&gt;

&lt;h1&gt;
  
  
  Parts
&lt;/h1&gt;

&lt;p&gt;Initiating acquisition of the necessary parts you will need to build your keyboard is a matter of following a checklist.  You will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Case - to contain all the other components in a presentable fashion&lt;/li&gt;
&lt;li&gt;PCB ("Printed Circuit Board") - the electronic hub that connects all of the switches to a chip, exposing (usually) a USB interface.  There are two major connector types: soldered, and "hot-swappable".  Hot-swappable sockets hold the switches in by friction, allowing one to replace switches without going through the hassle of desoldering/re-soldering.&lt;/li&gt;
&lt;li&gt;Plate - this is the part I most misunderstood: while the PCB has predefined holes that you will solder your switches to, each switch would have a little bit of wiggle-room if mounted to the PCB without a plate. The plate acts as a precise mount.  It stands off of the PCB about 1/4", and each switch "clicks" into the plate, with their connector prongs sticking out below to connect to the PCB.&lt;/li&gt;
&lt;li&gt;Switches - you have a lot of choices here!  Some have an extra post (for extra stabilization) that a few PCBs do not have corresponding holes for: they can be easily clipped off with wire-cutters, so don't worry too much about that.&lt;/li&gt;
&lt;li&gt;Stabilizers/Lubricant - stabilizers mechanically link the two sides of longer keys, so that when one extremity of the key is depressed, the whole switch depresses uniformly. You will want to lubricate the stabilizers for a smooth switch depression.  I forgot to buy lubricant, and built my board without it: a decision I now regret, as now that the switches are soldered to the board, getting at the stabilizers would mean de-soldering every switch (they are under the plate)!  I'm sure I can put some lubricant on a Q-tip and wiggle it between all of the pieces, but I really wish I had just done this right from the start.&lt;/li&gt;
&lt;li&gt;Keycaps - just make sure the caps you buy are compatible with the switches. There are a &lt;a href="https://thekeeblog.com/overview-of-different-keycap-profiles/"&gt;variety of keycap profiles&lt;/a&gt; to choose from.&lt;/li&gt;
&lt;li&gt;Supplies

&lt;ul&gt;
&lt;li&gt;Soldering iron&lt;/li&gt;
&lt;li&gt;Solder: 60/40 lead-based is recommended&lt;/li&gt;
&lt;li&gt;Solder sucker: get a high-quality one: the chances that you have to de-solder a switch during your build depend on a lot of factors, but I was left wishing that I had a more powerful solder-sucker than the one that came with the soldering-kit that I bought.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h1&gt;
  
  
  Assembly
&lt;/h1&gt;

&lt;p&gt;Once delivery of all the aforementioned parts have been made, the first thing you will want to do is test the PCB. If it is not working, you want to correct the situation before going any further! This turns out to be an elementary exercise: plug the PCB into your computer, open a "key-tester" (either online, or download), and, using tweezers, short the connectors where the switches will go, one at a time, checking that the corresponding switch is detected in the key-tester.&lt;/p&gt;

&lt;p&gt;One more important task presented itself to me when I took inventory of all my parts: the PCB I purchased (the DZ60) supports multiple layouts, and upon settling on a given layout, I laid each keycap out with respect to my chosen layout in order to make certain that everything lined up. This saved me any guess-work that would come later, and also aided me in locating the exact pin-locations where each switch would go (the DZ60 sometimes has multiple closely-positioned "alternate" holes to support small variances in switch-position).  Once I had that figured out, I was ready to start assembling the keyboard!&lt;/p&gt;

&lt;p&gt;The first things that must be mounted to the PCB are the stabilizers. There is one good way to learn to assemble your stabilizers: watch a video. These usually clip to the board.  Once they are in position, you are ready to start placing the switches.&lt;/p&gt;

&lt;p&gt;The switches clip into the plate, with their pins protruding downward into the holes on the PCB. You will want to take care to line them up precisely, especially if the plate allows any variance in position.  A few of mine ended up slightly crooked, and though someone else might notice, I wish I had spent even a slight bit more time on this. Start by clipping a few switches in each corner of the board, to give the build added stability, then add more switches.  Solder incrementally, then add more switches, rinse and repeat. Occasionally plug the board in and key-test the switches that you recently have added, just to be certain you have no "duds".&lt;/p&gt;

&lt;p&gt;Once your switches are soldered to the board, you can screw the PCB/plate to the case! There is nothing extra to mention here, and at this point, anticipation of the end of the project runs high!&lt;/p&gt;

&lt;p&gt;You can now perform what is most likely the most routing chore thus far: press each key-cap onto the switches! Plug the board in, and key-test once more for good measure.&lt;/p&gt;

&lt;h1&gt;
  
  
  Flashing
&lt;/h1&gt;

&lt;p&gt;Phew! If you thought that was a process, there is yet an extra mile you can go, depending on the capabilities of the PCB which you purchased. This is the part I love the most, and is, in my mind, one of the chief reasons for owning a customized keyboard: flash custom firmware to your keyboard!&lt;/p&gt;

&lt;p&gt;The PCB which I purchased supports QMK firmware (highly recommended), and the process for customizing the firmware can either be the "simple-mode", or the more thorough route:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the QMK online configurator (UI)&lt;/li&gt;
&lt;li&gt;Fork the QMK repo and follow their documentation for creating your custom firmware&lt;/li&gt;
&lt;li&gt;... there is yet one more option: use a combination of 1 + 2: the QMK command-line tools allow one to use the UI to produce a JSON configuration file that may be converted to C-code (via the command &lt;code&gt;qmk json2c ...&lt;/code&gt;).  This is the option I used.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Daunting? Yes, yet extremely rewarding. This is a project that I would highly recommend, especially if you spend the majority of your time typing. The keyboard is a typists' most necessary utensil, and being specially crafted removes yet more of the barrier know as the "human-computer-interface".&lt;/p&gt;

&lt;h1&gt;
  
  
  Post-Script: My Parts List
&lt;/h1&gt;

&lt;p&gt;PCB: &lt;a href="https://kbdfans.com/products/dz60-60-pcb"&gt;DZ60 revision 3.0 with Cherry stabilizers（6.25Ux1 2U x4)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Plate: &lt;a href="https://kbdfans.com/products/dz60-cnc-aluminum-plate"&gt;DZ60 CNC - Plate-A（left shift is 2U)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Case: &lt;a href="https://kbdfans.com/products/customized-gh60-aluminum-case"&gt;60% aluminum low profile case - Silver&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Switches: &lt;a href="https://kbdfans.com/products/switch-68-cherry-gateron-zealio"&gt;Gateron RGB brown&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Key-caps: &lt;a href="https://kbdfans.com/products/coming-soon-xda-dye-sub-keycaps"&gt;R1 XDA Dye-sub 60%&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Footnotes&lt;/strong&gt;&lt;br&gt;
[1] Jeff Atwood co-founded Stack Overflow&lt;br&gt;
[2] There are, in reality, many more switch choices than 5&lt;/p&gt;

</description>
      <category>keyboard</category>
      <category>mechanical</category>
      <category>diy</category>
    </item>
    <item>
      <title>`git switch`/`git restore`</title>
      <dc:creator>Jonathan Apodaca</dc:creator>
      <pubDate>Wed, 18 Dec 2019 14:20:01 +0000</pubDate>
      <link>https://dev.to/jrop/git-switch-git-restore-pd6</link>
      <guid>https://dev.to/jrop/git-switch-git-restore-pd6</guid>
      <description>&lt;p&gt;Saw some cool new Git commands show up!  What'y'all think?&lt;/p&gt;


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

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--i_qB0tUp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/860184226632802307/GjiGS87W_normal.jpg" alt="Mislav Marohnić profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Mislav Marohnić
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @mislav
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      The `git checkout` command was confusingly named and too overloaded in functionality.&lt;br&gt;“How do I switch to a branch?” - checkout&lt;br&gt;“How do I restore a file to a previous version?” - checkout&lt;br&gt;&lt;br&gt;Enter Git 2.23:&lt;br&gt;- `git switch &amp;lt;branch&amp;gt;`&lt;br&gt;- `git restore &amp;lt;file&amp;gt;`&lt;br&gt;&lt;br&gt;&lt;a href="https://t.co/S2PUuIOiIk"&gt;github.com/git/git/blob/m…&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      14:00 PM - 17 Dec 2019
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1206937217194741760" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1206937217194741760" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      929
      &lt;a href="https://twitter.com/intent/like?tweet_id=1206937217194741760" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      2580
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;I personally will be trying these out today as I work.&lt;/p&gt;

</description>
      <category>git</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>Why I am not switching to ZSH</title>
      <dc:creator>Jonathan Apodaca</dc:creator>
      <pubDate>Thu, 11 Jul 2019 17:22:11 +0000</pubDate>
      <link>https://dev.to/jrop/why-i-m-not-switching-to-zsh-4ngd</link>
      <guid>https://dev.to/jrop/why-i-m-not-switching-to-zsh-4ngd</guid>
      <description>&lt;p&gt;Recently Apple made a surprising announcement: due to licensing issues with Bash, they decided to &lt;a href="https://support.apple.com/en-us/HT208050"&gt;change the default shell on macOS from Bash to ZSH&lt;/a&gt;.  This change affects macOS Catalina and later.  It is no secret that many developers enjoy a UNIX-like development experience from the comfort of a Mac, and it is thus no surprise that in the wake of Apple's announcement, my Twitter feed began to fill up with a lot of &lt;a href="https://twitter.com/search?q=%23zsh&amp;amp;src=typd"&gt;ZSH buzz&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A lot of people are jumping ship to ZSH.&lt;/p&gt;

&lt;p&gt;Now, ZSH is certainly not new in the UNIX-shell space.  I have been hearing about this shell for some years now, and why I should be switching to it.  Heck, I even gave it a try in 2017.  I spent ample time customizing it, and enjoying its superior interactivity, customizability, and completion experience.  Ultimately, however, I switched back to Bash.  Why, you may ask, did I downgrade from ZSH to Bash?  There were numerous factors, but overall the big reason was that the extra wins that I gained in using ZSH over Bash were dwarfed by the fact that I was remoting into boxes at the time that had their default shells locked to Bash.  Given that most of the boxes I was using already had Bash, and Bash could be customized nearly to the same level of ZSH using &lt;a href="https://github.com/Bash-it/bash-it"&gt;Bash-it&lt;/a&gt;, I decided to unify my experience and converge back to one shell everywhere: Bash.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So what is the problem, exactly?&lt;/strong&gt; Don't get me wrong: I love ZSH, but in this case I just wanted to consolidate my efforts to customizing one shell.  In the end, I had almost all of the things I originally had with ZSH, but this time &lt;em&gt;everywhere&lt;/em&gt; with Bash-it.  Score? 90% win.  I'll take it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Weighing The "Cost of Adoption"
&lt;/h2&gt;

&lt;p&gt;You see, for most of us, we will continue to use Bash everyday, as long as we are on machines that have us locked to Bash shells.  It is therefore safe to assume -- at least in my case -- that Bash is not leaving my life anytime soon.  Therefore, it is worth me spending some amount of time customizing Bash to suite my needs.  We could even pretend that there is a semi-mathematical way to view this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CostOfAdopting(X) = CostOfMaintainingStatusQuo + CostOfCustomizing(X)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Do you see what the constant in that equation is?  Yep: maintaining the status-quo.  And what is the status quo in my case?  Customizing Bash.&lt;/p&gt;

&lt;p&gt;So, according to the above equation, for me to have incentive to adopt a new shell "X", it must have a low cost of customization.&lt;/p&gt;

&lt;p&gt;Hmmm. I wonder what shell that sounds like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Fish Shell
&lt;/h2&gt;

&lt;p&gt;Some time ago, I took the Fish Shell for a test drive, and promptly gave up when I realized it was not POSIX compliant.  For many this is a big deal, especially in one case: sourcing other script files.  While my daily workflow often still relies on being able to source a few script files, I have recently brought the Fish Shell back into my evaluation process.  I am excited to report that I am now using the Fish Shell as my daily (personal) shell!&lt;/p&gt;

&lt;p&gt;I have lead right into the "why", but I will state it anyway: Fish Shell has an extremely low cost of adoption in my case.  &lt;a href="https://twitter.com/hashtag/zeroconfig?src=hash"&gt;Zero Config&lt;/a&gt;, is all the rage right now, and for good reason: in this age of ephemeral everything, it needs to be easy to go from 0-60 &lt;em&gt;fast&lt;/em&gt;.  Fish Shell is no exception!  In its default state, it provides &lt;em&gt;so many&lt;/em&gt; sane defaults:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;syntax highlighting&lt;/li&gt;
&lt;li&gt;a killer &lt;a href="https://fishshell.com/docs/current/commands.html#fish_update_completions"&gt;completion story&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;del&gt;aliases&lt;/del&gt; -- who needs 'em when you have abbreviations [1]?&lt;/li&gt;
&lt;li&gt;a more sane and consistent language&lt;/li&gt;
&lt;li&gt;killer &lt;a href="https://fishshell.com/docs/current/index.html"&gt;documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;seriously: &lt;a href="https://fishshell.com/docs/current/commands.html#funced"&gt;&lt;code&gt;funced&lt;/code&gt;&lt;/a&gt;/&lt;a href="https://fishshell.com/docs/current/commands.html#funcsave"&gt;&lt;code&gt;funcsave&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sure, I cannot have Fish everywhere, but it is so easy to work with, that I am willing to commit to using it as my daily driver on my development machines.  Soon I hope to write more regarding my development setup, and the customizations I have been able to (easily) make to Fish.&lt;/p&gt;

&lt;p&gt;In the meantime, during limited exposure to Fish, the following are my favorite addons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jorgebucaran/fisher"&gt;jorgebucaran/fisher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/matchai/spacefish"&gt;matchai/spacefish&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jethrokuan/fzf"&gt;jethrokuan/fzf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jorgebucaran/fish-bax"&gt;jorgebucaran/fish-bax&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;[1] Fish abbreviations are similar in function to other shells' aliases, but they expand in place on the shell-edit-line, so you can see exactly what they are expanding to.  In my humble opinion, this is great because it removes the "magic" of aliases: especially if another developer is looking over your shoulder: no longer do you need to explain that &lt;code&gt;gc&lt;/code&gt; is short for &lt;code&gt;git commit&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>fish</category>
      <category>shell</category>
      <category>bash</category>
      <category>zsh</category>
    </item>
    <item>
      <title>Which do you prefer?: DSL or a well-designed Library?</title>
      <dc:creator>Jonathan Apodaca</dc:creator>
      <pubDate>Tue, 02 Apr 2019 14:26:15 +0000</pubDate>
      <link>https://dev.to/jrop/which-do-you-prefer-dsl-or-a-well-designed-library-2bli</link>
      <guid>https://dev.to/jrop/which-do-you-prefer-dsl-or-a-well-designed-library-2bli</guid>
      <description>&lt;p&gt;Lately, I am preferring libraries over DSLs more and more.  As I have ventured into learning Rust, it seems that all too many Crate authors abuse the (otherwise awesome) macro-system to create their own DSL.  If more library-developers spent their time on designing a well-put-together API, then I would have the benefit of everything else my IDE supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Syntax I am familiar with: nothing else to learn besides the API surface&lt;/li&gt;
&lt;li&gt;Autocomplete for discovery! (instead of switching between a Docs page and my text-editor)&lt;/li&gt;
&lt;li&gt;Inline documentation, if methods were documented&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The epitome of this philosophy would be &lt;a href="https://rethinkdb.com/docs/introduction-to-reql/"&gt;RethinkDB's query language&lt;/a&gt;, which is actually an API, and not its own query language!&lt;/p&gt;

&lt;p&gt;What do you all prefer?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>api</category>
      <category>library</category>
      <category>dsl</category>
    </item>
    <item>
      <title>Creating an Alerting Callback Plugin in Ansible - Part I</title>
      <dc:creator>Jonathan Apodaca</dc:creator>
      <pubDate>Fri, 03 Aug 2018 21:55:39 +0000</pubDate>
      <link>https://dev.to/jrop/creating-an-alerting-callback-plugin-in-ansible---part-i-1h0n</link>
      <guid>https://dev.to/jrop/creating-an-alerting-callback-plugin-in-ansible---part-i-1h0n</guid>
      <description>&lt;p&gt;Recently my department has been adopting Ansible into more of our workflows.  I like it so much that I converted my dotfiles repo over to use Ansible.  Notwithstanding our intrepid exhilaration at having so much more of our workflow inducted into an automated process, we recently were made privy to the fact that our visibility into what Ansible is actually doing to our infrastructure is less than perfect.&lt;/p&gt;

&lt;p&gt;Rewind to Monday morning, when I started receiving email complaints that one of our batch jobs had failed to complete.  After pouring over a monolithic log file, I discovered that the issue was that the user attempting to execute one of our scripts did not have directory-execute permissions on one of the parent directories that our script resided in.  It was a simple fix: &lt;code&gt;chmod ...&lt;/code&gt;.  The job was released and ran to completion.  Still, I wondered what had happened to change the directory permissions, especially since I knew that neither I, nor others, had been making any modifications to that particular system.&lt;/p&gt;

&lt;p&gt;Sure enough, after reaching out to our sysadmins, it was confirmed that a playbook had been run with the aforementioned undesired outcome.  No biggie: within 15 minutes the problematic playbook had been remedied.&lt;/p&gt;

&lt;p&gt;Crisis averted.   Conflict resolved.  However, I began to go over in my mind how we might avoid this situation in the future, and it gave me an idea: We (conveniently) have our host inventory separated into groups by teams.  Wouldn't it be convenient if there was a plugin that could, when a host had been changed, lookup variables on that host to see if any "alerting configuration" had been set, and alert the appropriate stakeholders for a given system?&lt;/p&gt;

&lt;h2&gt;
  
  
  Proposed Solution
&lt;/h2&gt;

&lt;p&gt;Let's start with my desired solution and work backward.  Say I have the following inventory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[webteam]&lt;/span&gt;
&lt;span class="err"&gt;webteam1.tld&lt;/span&gt;
&lt;span class="err"&gt;webteam2.tld&lt;/span&gt;

&lt;span class="nn"&gt;[systeam]&lt;/span&gt;
&lt;span class="err"&gt;systeam1.tld&lt;/span&gt;
&lt;span class="err"&gt;systeam2.tld&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would like to manage who I want to alert, upon a system change, via &lt;code&gt;group_vars&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;group_vars/webteam.yml&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;notify_on_change&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;somebody1@domain.tld&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;somebody2@domain.tld&lt;/span&gt;
  &lt;span class="na"&gt;slack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...slack config...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fantastic.  But Ansible does not come with such functionality out-of-the-box.  But wait a minute!  It turns out that developing a plugin is &lt;a href="https://docs.ansible.com/ansible/2.5/dev_guide/developing_plugins.html"&gt;relatively painless&lt;/a&gt;.  It turns out that what I want to develop is called a "callback plugin".  A callback plugin is a Python class that gets called during different phases of Ansible's lifecycle.  The shortest callback plugin we could write would be the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ansible.plugins.callback&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CallbackBase&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CallbackModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CallbackBase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;CALLBACK_VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;
  &lt;span class="n"&gt;CALLBACK_TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'aggregate'&lt;/span&gt;
  &lt;span class="n"&gt;CALLBACK_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'custom'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This callback plugin will not do anything, but there are a few takeaways to note already: this callback plugin is advertising that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it wishes to consume the v2 API&lt;/li&gt;
&lt;li&gt;its type is &lt;strong&gt;&lt;code&gt;aggregate&lt;/code&gt;&lt;/strong&gt;: a callback plugin's type is important.  More specifically, what type a callback plugin is &lt;strong&gt;not&lt;/strong&gt; is even more important, as &lt;strong&gt;only one callback plugin of type &lt;code&gt;stdout&lt;/code&gt; can be enabled at once&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;its name is &lt;code&gt;custom&lt;/code&gt;.  We will refer to the callback plugin later by this name.  As far as I know, the filename (e.g., &lt;code&gt;whatever.py&lt;/code&gt;) is not used to refer to the callback plugin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As previously stated, multiple callbacks can be active at once, but only one callback plugin of type &lt;code&gt;stdout&lt;/code&gt; can be active at once.  So how do we enable a callback plugin?  Easy, jump into your friendly &lt;code&gt;ansible.cfg&lt;/code&gt;, and set the following config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[defaults]&lt;/span&gt;
&lt;span class="c"&gt;# ...
# comma separate callback plugins to whitelist:
&lt;/span&gt;&lt;span class="py"&gt;callback_whitelist&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;custom&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will instruct Ansible to enable the callback plugin that we have defined.  But where do these callback plugins go?  Once again, Ansible has an easy answer to this: in the &lt;code&gt;callback_plugins/&lt;/code&gt; directory, either 1) within one of the roles you include, or 2) adjacent to your playbook(s).  So far, a possible directory structure could look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-playbook-folder/
  callback_plugins/
    my-callback-plugin.py
  ansible.cfg
  playbook.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building the Callback Plugin
&lt;/h2&gt;

&lt;p&gt;Alright, so what exactly should our plugin do?  To keep things simple, we will specify the behavior of our plugin to do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Slack as a notification service&lt;/li&gt;
&lt;li&gt;Do not send a notification unless a Task reports that it "changed" the target system&lt;/li&gt;
&lt;li&gt;If a change occurred, lookup the stakeholders for a given system using the particular hosts host-/group- vars.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where things start to get slightly hairy: in order to inspect the state of the Play(s) and Task(s), we will be digging into Ansible internals.  Thankfully, there exists a great community to support you in this endeavor, but let us start by mentioning that all the methods that your callback plugin can override can be found in &lt;a href="https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/callback/__init__.py"&gt;in the &lt;code&gt;CallbackBase&lt;/code&gt; class&lt;/a&gt;.  After a bit of digging around, and a little &lt;a href="https://stackoverflow.com/q/51640444/263986"&gt;waiting for an answer on a StackOverflow question&lt;/a&gt;, I settled on the following base code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ansible.plugins.callback&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CallbackBase&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CallbackModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CallbackBase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;CALLBACK_VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;
  &lt;span class="n"&gt;CALLBACK_TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'aggregate'&lt;/span&gt;
  &lt;span class="n"&gt;CALLBACK_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'is'&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;v2_playbook_on_play_start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_variable_manager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;v2_runner_on_ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_vars&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s"&gt;'ansible_check_mode'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="n"&gt;host_vars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_vars&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s"&gt;'hostvars'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;'notify_on_change'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;host_vars&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_changed&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
      &lt;span class="k"&gt;pass&lt;/span&gt;
      &lt;span class="c1"&gt;# TODO: Post Slack message
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;WARNING&lt;/em&gt;: I do NOT know Ansible internals and have found the above code to "work" after a lot of trial and error.  My &lt;em&gt;impression&lt;/em&gt; is that host and group vars are instantiated and merged on a per-play basis.  That is why the variable manager is captured "on play start" and stored for later use.  Later, when a task finishes, we use the variable manager to check 1) whether Ansible is running in &lt;code&gt;--check&lt;/code&gt;-mode, and then later to retrieve the specific host's &lt;code&gt;notify_on_change&lt;/code&gt; configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Bit of Debugging Strategy
&lt;/h2&gt;

&lt;p&gt;So this is the part of the article where I get irrationally angry that Python does not support type-annotations.  If it did, I might be able to engage in rapid discovery surrounding exactly what I could pull out of the parameters being passed to my callback plugin's hooks.  Who knows, I might even be able to use IDE hints like IntelliSense or Content Assist to quickly retrieve what I need.&lt;/p&gt;

&lt;p&gt;If you could see me at this moment, my face red with rage, sweat dripping down my neck, you might notice that I am calming down and reminding myself of the merits of scripting languages.  In fact, one of my favorite rapid-development-tools is Node.JS.  At this point, I set my bias aside and trudge on.  Time to pull out some debugging-fu.&lt;/p&gt;

&lt;p&gt;After a little poking around, there is a one-line technique you can use to suspend &lt;em&gt;any&lt;/em&gt; Python program and invoke an interactive debugger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pdb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;pdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_trace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fantastic!  I can now drop this into my plugin at a point where I would like to inspect vars and iterate (&lt;em&gt;very slowly&lt;/em&gt;) until I get some code that works.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CallbackModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CallbackBase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="c1"&gt;# ...
&lt;/span&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;v2_runner_on_ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pdb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;pdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_trace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# ... same code as before
&lt;/span&gt;  &lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if I invoke Ansible like normal, it will run until it calls my &lt;code&gt;v2_runner_on_ok&lt;/code&gt; method, and it will immediately suspend, dropping me into a request-evaluate-print-loop (REPL) where I can interactively step through my code and inspect variables in my scope!&lt;/p&gt;

&lt;h2&gt;
  
  
  That's All For Now
&lt;/h2&gt;

&lt;p&gt;I'm going to end this article here, as it is already turning out longer than I had planned.  Even if it was not for that, I would still have to end it, as that is all I have implemented at this point.  Hopefully I will finish my callback plugin soon (or one of you will beat me to it), and I will share my findings and code in a "Part II".&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>callback</category>
      <category>plugin</category>
      <category>alerting</category>
    </item>
    <item>
      <title>What I Learned this Week (March 30, 2018)</title>
      <dc:creator>Jonathan Apodaca</dc:creator>
      <pubDate>Fri, 30 Mar 2018 20:48:06 +0000</pubDate>
      <link>https://dev.to/jrop/what-i-learned-this-week-march-30-2018-1ck3</link>
      <guid>https://dev.to/jrop/what-i-learned-this-week-march-30-2018-1ck3</guid>
      <description>&lt;h2&gt;
  
  
  Directed Graphs: &lt;a href="https://en.wikipedia.org/wiki/Connected_component_%28graph_theory%29"&gt;Connected Components&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I started researching algorithms for finding Connected Components in a Directed Graph, for the purpose of being able to more efficiently add visibility into large dependency graphs (particularly in a database; e.g., job &lt;code&gt;J&lt;/code&gt; uses table &lt;code&gt;T&lt;/code&gt; to produce &lt;code&gt;T'&lt;/code&gt;...).  This information could be used to develop more time-efficient schedules that produce data at my institution.  Having an idea how I would find "islands" in our dependency graph, I decided to research it and try to learn something about Graph Theory.&lt;/p&gt;

&lt;p&gt;Further reading for me: &lt;a href="https://en.wikipedia.org/wiki/Strongly_connected_component"&gt;Strongly Connected Components&lt;/a&gt; is a method to determine areas in a graph that are more strongly connected than others.  This seems pretty cool.  I wish I had a practical use for it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Relearn: &lt;a href="https://en.wikipedia.org/wiki/Topological_sorting"&gt;DAG: Topological Sort&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Regarding the above problem, I implemented a topological sort of our dependency graph to find all of the jobs that can run in parallel to make our scheduling more efficient.  Finally: I used something from a class in my job!  It is always satisfying using something that used to be homework.&lt;/p&gt;

&lt;h2&gt;
  
  
  libclang
&lt;/h2&gt;

&lt;p&gt;Apparently, you can leverage C frontend built for LLVM via the libclang library.  See &lt;a href="https://shaharmike.com/cpp/libclang/"&gt;this post&lt;/a&gt; for more details.  Utilizing this, you can parse C++, traverse the AST--Great stuff.&lt;/p&gt;

&lt;p&gt;There are even &lt;a href="https://www.npmjs.com/package/libclang"&gt;nodejs bindings&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  RPython
&lt;/h2&gt;

&lt;p&gt;If you are wanting to produce a VM that supports interpreting &lt;em&gt;and&lt;/em&gt; a Just-in-Time compilation, RPython is pretty amazing.  &lt;a href="http://tratt.net/laurie/blog/entries/fast_enough_vms_in_fast_enough_time.html"&gt;This article&lt;/a&gt; is a very interesting read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TLDR;&lt;/strong&gt; RPython is a VM generator: it consists of a language (a subset of Python) and a tool that analyzes &lt;code&gt;(interpreter code + hints)&lt;/code&gt; and produces a (C-based) VM (interpreter + JIT) for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shibboleth
&lt;/h2&gt;

&lt;p&gt;Recently I was brought on as a resource on a project in my department that requires integration with the Single Sign-On service Shibboleth.  Fun-fact: &lt;a href="https://biblehub.com/esv/judges/12.htm"&gt;Shibboleth is mentioned in the Bible&lt;/a&gt; (Judges 12:6) as a way that the ancient Israelites enforced border-control during a time of hostility amongst their own clans.&lt;/p&gt;

&lt;p&gt;Shibboleth as a product is highly configurable, and a semi-complex beast.  While I have previous experience with setting it up, it seems there is always something else to learn about it.  Most of my learning on this front has been spent in frustration whilst attempting to make it work in an equally complex, load-balanced environment.&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>What I Learned this Week (March 23, 2018)</title>
      <dc:creator>Jonathan Apodaca</dc:creator>
      <pubDate>Mon, 26 Mar 2018 16:48:20 +0000</pubDate>
      <link>https://dev.to/jrop/what-i-learned-this-week-march-23-2018-2a3h</link>
      <guid>https://dev.to/jrop/what-i-learned-this-week-march-23-2018-2a3h</guid>
      <description>&lt;p&gt;This week's list will be a bit shorter, as I was caught up in a lot of administrative tasks this week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker: Service vs. Stack
&lt;/h2&gt;

&lt;p&gt;When using Docker in Swarm Mode, a stack describes multiple "services" to run in your cluster.  As &lt;code&gt;docker-compose&lt;/code&gt; is to &lt;code&gt;docker run&lt;/code&gt;, so is a Docker stack to a Docker service.  Incidentally, stacks are also described in a very similar manner via YML as you would in a &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;A Docker service is a collection of homogeneous containers, that is, with the same &lt;code&gt;image:tag&lt;/code&gt;.  Docker Swarm will replicate the service across multiple nodes according to the rules you set.&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>What I Learned This Week (March 16, 2018)</title>
      <dc:creator>Jonathan Apodaca</dc:creator>
      <pubDate>Fri, 16 Mar 2018 17:12:31 +0000</pubDate>
      <link>https://dev.to/jrop/what-i-learned-this-week-march-16-2018--2ee3</link>
      <guid>https://dev.to/jrop/what-i-learned-this-week-march-16-2018--2ee3</guid>
      <description>&lt;p&gt;One of the things I value highly where I work is that, each week, our Director emails out a "What I Learned This Week".  It got me thinking that a good way for me to reflect on what I have learned would be for me to publish the same thing each Friday.&lt;/p&gt;

&lt;p&gt;So without further delay, I present to you the things that I learned this week:&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Themes
&lt;/h2&gt;

&lt;p&gt;We recently started our DevOps committee where I work, and we have been discussing what our current direction will be for the next six months.  With that in mind, I have personally been reviewing the following technologies for possible recommendation as things we can look into:&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes via Minikube
&lt;/h2&gt;

&lt;p&gt;Last time I looked into learning Kubernetes (affectionately abbreviated as &lt;code&gt;k8s&lt;/code&gt; by the community), it was prohibitively difficult to get into and learn due to having to spin up a small cluster sandbox merely to experiment with.  Imagine my excitement at learning that a tool, &lt;a href="https://github.com/kubernetes/minikube"&gt;minikube&lt;/a&gt;, exists that allows you to play with Kubernetes locally on your development machine!  Seriously, go install it and start playing with Kubernetes.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.hashicorp.com/"&gt;HashiCorp&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;HashiCorp is famous for developing the amazing tool, Vagrant.  Well, it turns out that they have a ton of other awesome projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform - represent your infrastructure as code&lt;/li&gt;
&lt;li&gt;Nomad - a deployment tool that is awesome&lt;/li&gt;
&lt;li&gt;Vault - secret management tool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The major win with these tools is they are modular and modularly adopt-able.  That is also their weakness, however: there is more to set up in order to play with them.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="http://docs.ansible.com/ansible/latest/index.html"&gt;Ansible&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This week I also rediscovered Ansible.  By rediscovered, I mean I delved more into how it works and fell in love with it all over again.  We are already using this tool at my job, but up until now, it has mostly had major wins on our Systems Support team.  This week, it finally "clicked" how Ansible configuration works, and it is now no longer such a (great) mystery.  I will definitely be integrating this into more of my workflows more often.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;I am just going to keep these short and sweet, so that's all folks!&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>devops</category>
    </item>
    <item>
      <title>Pratt Parsing</title>
      <dc:creator>Jonathan Apodaca</dc:creator>
      <pubDate>Fri, 11 Aug 2017 21:35:45 +0000</pubDate>
      <link>https://dev.to/jrop/pratt-parsing</link>
      <guid>https://dev.to/jrop/pratt-parsing</guid>
      <description>&lt;p&gt;Ever since I started programming, I have tried to come up with interesting projects for myself, even if they are purely for educational purposes.  The joy of programming is the goal sometimes.  Couple with that an opportunity to learn a new concept and you have got my attention!  Some of my first projects were about as simple as they get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When wanting to learn Java Swing, I created a number guessing game. ("I'm thinking of a number between 1 and 10...you have 10 tries to guess it"...)&lt;/li&gt;
&lt;li&gt;When wanting to learn how to interact with XML, I created a simple "Mail" program in .NET that I used to communicate with my siblings, storing each mailbox in a flat XML file (eesh, not a very robust DB store)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was a while ago.  Since then, I would hope that my skills have improved and that I have been able to solve the problems since then with more elegance and maturity.&lt;/p&gt;

&lt;p&gt;However, there has always been one project that I could never finish: writing a calculator.  I never was good at parsing text.  Funny thing is, now that I look back on my methodologies, in hindsight I can acknowledge that I was on the right track in some respects, and way off base with others.  The truth is, it takes some forethought when creating a calculator.  This post will document where I currently am at in my understanding of how to tackle this seemingly simple task.  I do not claim to have arrived at an expert understanding.&lt;/p&gt;

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

&lt;p&gt;Let us start with the end goal and work backward from there.  In the end, I want to produce a program--let it go under the moniker &lt;code&gt;calc&lt;/code&gt;-- that I can invoke like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;calc
Welcome to Calculator 1.0
Please enter an expression on the following line:
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 1 + 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;-3&lt;/span&gt; + 2^+3^2
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; The answer is 507
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 1 + 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-3&lt;/span&gt; + 2^+3^2&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; The answer is 1019
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hmm, let us try and draw some specifications out of this theoretical demo:&lt;/p&gt;

&lt;p&gt;We need to support things like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;operator precedence&lt;/li&gt;
&lt;li&gt;right associativity (&lt;code&gt;1^2^3&lt;/code&gt; means &lt;code&gt;1^(2^3)&lt;/code&gt;, not &lt;code&gt;(1^2)^3&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;some operators are both infix &lt;em&gt;and&lt;/em&gt; unary

&lt;ul&gt;
&lt;li&gt;in the expression &lt;code&gt;1 - 2&lt;/code&gt;, the &lt;code&gt;-&lt;/code&gt; is an infix operator&lt;/li&gt;
&lt;li&gt;in the expression &lt;code&gt;-2&lt;/code&gt;, the &lt;code&gt;-&lt;/code&gt; is a unary operator&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;use of parenthesis can override operator precedence&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Back when I first tried to tackle this problem, #4 seemed to be the easiest bullet to tackle.  When I first attempted to code this program, I had a first pass that would find groups of parenthesis (by finding matching parenthesis), and pull those groups out into what started to look like an abstract syntax tree.  But in the end, that was as far as I got before I had to abandon the project for my number-guessing game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Work, dang-it
&lt;/h2&gt;

&lt;p&gt;Shortly after I realized that the problem of operator precedence parsing was not as trivial as I had assumed, I found the splendid tool called ANTLR.  ANTLR is a parser-generator that takes a grammar as input, and produces code that parses the text that conforms to the grammer into an abstract syntax tree.  This means you can write a grammar such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NUM: '\d+';
e:
    NUM
  | '-' e
  | e '^' e &amp;lt;assoc=right&amp;gt;
  | e '*' e
  | e '/' e
  | e '+' e
  | e '-' e
  ;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ANTLR can use a grammar akin to this to produce a parser for you.  This parser will take input text, say &lt;code&gt;1 + 2 * 3&lt;/code&gt;, and produce an abstract syntax tree like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  +
 / \
1   *
   / \
  2   3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that this tree follows the precedence rules that you define in your grammar.&lt;/p&gt;

&lt;p&gt;Yay!  Creating a parser was pretty easy after all!  Well-- until your curiosity gets the better of you and you wonder how in the heck this amazing thing called a parser generator works.  Also, try not to look at the generated parser code: it gets--gnarly.&lt;/p&gt;

&lt;p&gt;At the end of the day I heard how more and more languages are using hand-coded recursive decent parsers, and I got curious.  How &lt;em&gt;would&lt;/em&gt; I code that calculator app with code that I wrote 100% myself?&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet my friend, Vaughan Pratt
&lt;/h2&gt;

&lt;p&gt;Well, he is not actually my friend, but I cannot help but think of him on friendly terms after learning his strategy for top-down-operator-precedence parsing.  At a high level, Pratt parsing works by scanning the input tokens, and classifying them into two categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;operators that operate to the right, with &lt;strong&gt;no&lt;/strong&gt; left-context

&lt;ul&gt;
&lt;li&gt;An example of this would be unary minus &lt;code&gt;-1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Pratt calls the specification of how an operator consumes to the right with &lt;strong&gt;no&lt;/strong&gt; left-context its "Null-Denotation", or "NUD" for short&lt;/li&gt;
&lt;li&gt;For example, &lt;code&gt;nud('-')&lt;/code&gt; could produce an AST node &lt;code&gt;Negative(operand=...)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;operators that operate from left-to-right, on two operands

&lt;ul&gt;
&lt;li&gt;This is any simple infix operator; for example: &lt;code&gt;1 + 2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Pratt calls the specification of how an operator consumes to the right with a left-context its "Left-Denotation", or "LED" for short&lt;/li&gt;
&lt;li&gt;For example, &lt;code&gt;led(left, '-')&lt;/code&gt; could produce an AST node &lt;code&gt;Subtraction(left=left, right=...)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice that in this case we are scanning left-to-right.  What this means is we are forming the AST as we are scanning the input.  For example, let us Pratt-parse the string &lt;code&gt;1 + 2 + 3&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;We start at our first token, and note how we cannot see the rest (here denoted with underscores "_"):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 _ _ _ _
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we first start out, we have no left-context, so we take the NUD of the first token: &lt;code&gt;NUD(1)&lt;/code&gt;.  In this simple case, the NUD of a number is the number itself, so the &lt;code&gt;1&lt;/code&gt; remains a &lt;code&gt;1&lt;/code&gt;.  Let us move on and look at the next token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 + _ _ _
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time around, we have a left-context (which is &lt;code&gt;1&lt;/code&gt;), so we will take &lt;code&gt;LED(1, '+')&lt;/code&gt;.  For now, let us say that all LED needs to do is consume the next number and return the subtree so far.  In this case, LED will produce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  + _ _
 / \
1   2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This subtree is our new "left".  We will then pop the next operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  + + _
 / \
1   2

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

&lt;/div&gt;



&lt;p&gt;Okay, the operator is "+", and we have a left-context (our subtree), so we take &lt;code&gt;LED(left, '+')&lt;/code&gt; again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    +
   / \
  +   3
 / \
1   2

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

&lt;/div&gt;



&lt;p&gt;We are now at the end of our input, and so parsing stops and we have our abstract syntax tree!  We could pseudo-code-ize our process so far:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;eof&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;led&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, so good.&lt;/p&gt;

&lt;h2&gt;
  
  
  Precedence though...
&lt;/h2&gt;

&lt;p&gt;Okay we have a problem.  If we execute our pseudo-code on the string &lt;code&gt;1 + 2 * 3&lt;/code&gt;, we will get the following abstract syntax tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    *
   / \
  +   3
 / \
1   2

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

&lt;/div&gt;



&lt;p&gt;This is not correct: it would evaluate the "+" before the "*", so we have a problem.  What we need is some way to encode operator precedence.  In fact, the problem lies when we are here with the parsing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
1 + _ _ _

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

&lt;/div&gt;



&lt;p&gt;We do not know that a multiplication is coming up and that we need to parse the &lt;code&gt;2 * 3&lt;/code&gt; as its own expression.&lt;/p&gt;

&lt;p&gt;Hmm, there were some key words in there that we should pay attention to: we need to parse the &lt;code&gt;2 * 3&lt;/code&gt; as its own expression.  What this implies is that our &lt;code&gt;expr()&lt;/code&gt; function needs to be called recursively.  Currently, we have defined LED like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;LED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What if it were more like this?:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;LED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                                                 &lt;span class="o"&gt;^^^^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, when we want the LED of an operator, we do not want to call &lt;code&gt;expr()&lt;/code&gt; and consume the rest of the input.  We will want it to stop at some point.  For example, say we are parsing the string &lt;code&gt;1 * 2 - 3&lt;/code&gt; and we are at this point in the parsing process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 * _ _ _

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

&lt;/div&gt;



&lt;p&gt;In this case, we only want &lt;code&gt;expr()&lt;/code&gt; to parse the next number, not the entire expression &lt;code&gt;2 - 3&lt;/code&gt;.  We need a way to signal &lt;code&gt;expr()&lt;/code&gt; when to stop.&lt;/p&gt;

&lt;p&gt;But what is the pattern for when we want to stop/run?  I will give you a clue: it has to do with the operator precedence...&lt;/p&gt;

&lt;h2&gt;
  
  
  Power in precedence
&lt;/h2&gt;

&lt;p&gt;It turns out that as humans, we can pick out operator precedence pretty intuitively.  We look at an expression such as &lt;code&gt;1 + 2 * 3&lt;/code&gt;, and we know that the &lt;code&gt;*&lt;/code&gt; in this expression &lt;em&gt;binds&lt;/em&gt; with the 2 and 3.  The plus &lt;em&gt;binds&lt;/em&gt; with the 1 and the sub-expression.  Let us give that a name.  Let us give each operator a &lt;strong&gt;binding power&lt;/strong&gt;.  In this case, &lt;code&gt;*&lt;/code&gt; has a greater &lt;strong&gt;binding power&lt;/strong&gt; than &lt;code&gt;+&lt;/code&gt;.  In fact, let us put some numbers to this so-called &lt;strong&gt;binding power&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;+&lt;/code&gt; - &lt;code&gt;10&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;*&lt;/code&gt; - &lt;code&gt;20&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In fact, these numbers are arbitrary.  What is important is that the binding power for &lt;code&gt;*&lt;/code&gt; is greater than the binding power for &lt;code&gt;+&lt;/code&gt;.  But how does this help us?  Well, when we are parsing an expression at a specific binding power, we need to stop when we encounter a binding power less than the one we are currently parsing for.  In other words, when we encounter lower binding powers, we bail and call it quits.&lt;/p&gt;

&lt;p&gt;Let us put this verbiage into our pseudo-code (&lt;code&gt;rbp&lt;/code&gt; stands for "right binding power"):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rbp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;rbp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;led&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is actually our final version of &lt;code&gt;expr()&lt;/code&gt;.  This is what a Pratt parser is based around.  In the end, what we have to provide are all the binding powers, and define NUD/LED for each operator.&lt;/p&gt;

&lt;p&gt;What this means is that we need to redefine LED from above.  Whereas it was previously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;LED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It now needs to be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;LED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
                                                      &lt;span class="o"&gt;^^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let us follow how this works through and example.  Let us parse &lt;code&gt;1 * 2 + 3&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// call expr(rbp = 0)
1 _ _ _ _
// take NUD(1)
1 * _ _ _
// take LED(1, '*') = Tree(1, '*', right=expr(bp('*')))
//                  = Tree(1, '*', right=expr(20))
  * _ _ _
 / \
1   expr(20)


// start recursion: expr(20)
// take NUD(2)
2 + _
// 20 &amp;lt; bp('+') ?
// nope, return left so far (2)

// resume previous recursion
  * + _
 / \
1   2
// 0 (current rbp) &amp;lt; bp('+')?
// yes! continue parsing...
// take LED(left, '+') = Tree(left, '+', right=expr(bp('+')))
//                     = Tree(left, '+', right=expr(10))
    +
   / \
  *   expr(10)
 / \
1   2

// start recursion: expr(10)
// abbreviated: will return 3

// resume previous recursion
    +
   / \
  *   3
 / \
1   2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully, that was not too hard to follow.  My mind bends every time I follow this process through to completion.  But we are done!  Note how operator precedence is correctly preserved in the produced abstract syntax tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not to be pedantic, but how do you handle right associativity?
&lt;/h2&gt;

&lt;p&gt;This turns out to be almost too easy.  Let us say we want our exponentiation operator to be right associative (as it should).  If we implement &lt;code&gt;LED(left, '^')&lt;/code&gt; as we have implemented our other infix operators, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;LED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the right binding power will kick us out when we hit an operator of lesser (or equal!) binding power, including our own, meaning that we will parse &lt;code&gt;1^2^3&lt;/code&gt; as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    ^
   / \
  ^   3
 / \
1   2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, if we tweak our definition of LED to be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;LED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                                              &lt;span class="o"&gt;^^^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the right binding power passed into &lt;code&gt;expr(...)&lt;/code&gt; will be less than the binding power of '^'.  This will produce the correct tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    ^
   / \
  1   ^
     / \
    2   3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait, right associativity was that easy??&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Pratt parsers are awesome, and once you understand what is going on under the hood, they are really simple.  As it turns out, one of my side-projects right now is a parser that parses JavaScript.  With Pratt parsing, even parsing JSX is not too bad.  If you would like to see my Pratt-parser-calculator that I finished (finally, after all these years), then hop on over to &lt;a href="https://github.com/jrop/pratt-calculator"&gt;the project's GitHub&lt;/a&gt;.  Thanks for reading!&lt;/p&gt;

</description>
      <category>pratt</category>
      <category>parser</category>
      <category>tdop</category>
      <category>precedence</category>
    </item>
  </channel>
</rss>
