<?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>chrisdinger.com</title>
    <description>chrisdinger.com</description>    
    <link>https://chrisdinger.com</link>
    <atom:link href="https://chrisdinger.com/feed.xml" rel="self" type="application/rss+xml" />
    
      <item>
        <title>Controlling my garage door with Elixir and Nerves</title>
        <dc:creator></dc:creator>
        <description>&lt;p&gt;I’ve wanted to learn Elixir for years now and have been looking for a little project to explore with. The best way to learn a new language for me is to solve a concrete problem I have. Toy apps don’t work for me.&lt;/p&gt;

&lt;p&gt;I’ve also been following the Nerves project and am really interested in using Elixir/Erlang OTP model for embedded projects. Elixr/Erlang’s &lt;em&gt;expect failures and deal with them&lt;/em&gt; mantra really fits well with the embedded world. I’ve dabbled with embedded development for a long time and have always preferred to reach for C. It’s super lightweight and tiny, but also super easy to screw up. With the availability and low cost of more powerful systems like Raspberry Pi Zero, you can more easily afford to move up to a higher-level language like Elixir without making too many trade-offs (yes I know MicroPython exists).&lt;/p&gt;

&lt;p&gt;Automating my garage door seems like the perfect excuse to finally do something with Elixir and Nerves. I can’t see my garage door from inside my house and I’m always wondering if it’s open or closed. I could buy a $50 retail sensor/switch to do this, but that’s not fun and it won’t help me learn Elixir. I’ve also used a homebridge setup to do this in the past, but the homebridge ecosystem is maddening. Any given plugin has at least a dozen variants and finding the one that’s been updated in the past decade is a chore.&lt;/p&gt;

&lt;h2 id=&quot;hardware&quot;&gt;Hardware&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Raspberry Pi Zero W (the one with wireless networking)&lt;/li&gt;
  &lt;li&gt;5-volt relay module&lt;/li&gt;
  &lt;li&gt;Magnetic reed switch&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;software&quot;&gt;Software&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Elixir and Nerves for firmware&lt;/li&gt;
  &lt;li&gt;Home Assistant&lt;/li&gt;
  &lt;li&gt;MQTT server (via Home Assistant)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;firmware&quot;&gt;Firmware&lt;/h2&gt;

&lt;!--&lt;img src=&quot;/images/door_sensor.jpg&quot; style=&quot;height: 400px; float: right; margin: 1em 0 3em 3em;&quot;&gt;--&gt;

&lt;p&gt;Let’s start with the sensor. Something that will tell me if the garage door is open or closed. The simple way to do this is with a magnetic reed switch. It’s a switch that has two contacts and a long thin metal reed and opens and closes the switch in the presence of a strong magnet. I mounted the sensor on the frame of the garage door and the magnet onto the door itself.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/door_sensor.jpg&quot; style=&quot;height: 400px;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The one I’m using has screw terminals for NC (normally closed) and NO (normally open) configurations. I’m using it in NO mode so the system will fail open. That way if the wires become disconnected for any reason, it will indicate that the door is open. What I don’t want is for something to fail and indicate the the door is closed when it isn’t.&lt;/p&gt;

&lt;p&gt;Nerves has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CircuitsGPIO&lt;/code&gt; for interacting with your board’s general purpose input/output pins; that’s what we’ll use here. Install it by adding it to your mix dependencies:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:circuits_gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;~&amp;gt; 0.4&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve created an Elixir module called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Garage.DoorSensor&lt;/code&gt; that implements the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenServer&lt;/code&gt; callbacks required for this. The GenServer abstraction is always the piece I’ve struggled with in Elixir. Here’s the minimum setup needed to sense the door state. It’s all GenServer callbacks:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Garage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DoorSensor&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;GenServer&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Logger&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;@sensor_pin&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;26&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;GenServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;__MODULE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;__MODULE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Circuits&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;GPIO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;@sensor_pin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;pull_mode:&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:pullup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Circuits&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;GPIO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_interrupts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:both&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;gpio:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handle_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:circuits_gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@sensor_pin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Garage door is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init&lt;/code&gt; funciton is called when the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenServer&lt;/code&gt; process starts. It sets up the pin our sensor is connected to and configures an interrupt (a function that will be called when the pin state changes) to handle the state changes. What isn’t super clear here for Elixir/Nerves newcomers is that the interrupt is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenServer&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;info&lt;/code&gt; message. This isn’t explicitly documented anywhere. I had to look at the CircuitsGPIO examples and search though other Github repositories to sort this out.&lt;/p&gt;

&lt;p&gt;I’ve connected to two leads of the sensor to pin 26 and ground. When the switch/door is open, pin 26 is connected to ground.&lt;/p&gt;

&lt;h2 id=&quot;activating-the-opener&quot;&gt;Activating the opener&lt;/h2&gt;

&lt;p&gt;Controlling the door opener relay is a lot simpler—it’s just a function call (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Circuits.GPIO.write&lt;/code&gt;). I did wrap this up into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenServer&lt;/code&gt; module in order to store the GPIO reference:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Garage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DoorSwitch&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;GenServer&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Logger&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;@switch_activation_time_in_ms&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;@switch_pin&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;activate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;GenServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;__MODULE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:activate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;GenServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;__MODULE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;__MODULE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Door switch init...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Circuits&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;GPIO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;@switch_pin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;gpio:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handle_cast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:activate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;gpio:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Activating door switch...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Circuits&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;GPIO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;@switch_activation_time_in_ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Circuits&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;GPIO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gpio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There’s also the on/off timing logic. We can’t just turn the relay on—that’d be like mashing down the opener button and never letting go. We want to simulate a button press. I chose 500ms (half a second) and it seems to work fine.&lt;/p&gt;

&lt;p&gt;There are more &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenServer&lt;/code&gt; callbacks here. Just like in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Garage.DoorSensor&lt;/code&gt;, we have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;start_link&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init&lt;/code&gt;. In this module we’re using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenServer.cast&lt;/code&gt; because we don’t need a response. It’s just call-and-forget.&lt;/p&gt;

&lt;p&gt;The other difference here is that we’ve added a public API—the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;activate&lt;/code&gt; function. This is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenServer&lt;/code&gt; pattern you’ll encounter: define public API functions and implement them in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenServer&lt;/code&gt; callbacks using the appropriate callback flavor (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;call&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cast&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;info&lt;/code&gt;).&lt;/p&gt;

&lt;h2 id=&quot;tying-it-all-together&quot;&gt;Tying it all together&lt;/h2&gt;

&lt;p&gt;The other bit of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenServer&lt;/code&gt; that has confused me in the past is how to start these processes up. My poor old programmer brain sees the world in OO. I’ve been thinking of these Elixir processes as object instances. You “instantiate” them and send them messages, right? So where do these things get intstantiated? The supervision tree. Your generated Nerves project skeleton will come an application file (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Garage.Application&lt;/code&gt; in my case). This is where these child processes are defined and configured. Here’s an abbreviated version of what I’m using:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Garage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Application&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Application&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;strategy:&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:one_for_one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Garage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Supervisor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;children&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Garage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DoorSensor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Garage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DoorSwitch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;Supervisor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!--
## Outline

- Motivation
  - Want to learn Elixir and Nerves
- Problem
  - I want to know if my garage door is open (I can&apos;t see if from inside the house).
  - I can spend $75 on a retail sensor/switch. But that is not fun and I won&apos;t learn anything.
  - Bonus: activate garage door, sense service door (while we&apos;re at it)
- Hardware
  - Raspberry Pi Zero W
  - 5-volt relay module
  - Magnetic reed switch(es)
- Software
  - Elixir/Nerves firmware
  - Home Assistant
  - MQTT server (via Home Assistant)
- Firmware design
  - Sensor:
  - Here&apos;s where I&apos;m revealing my noob-osity noob-aciousness: I&apos;m not sure what the idomatic way to do this is.
    - Set up Circuits GPIO interrupt to detect door sensor state changes (rising and falling)
      - GenServer confusion: docs were unclear
      - image of rising, falling edges?
    - Configure sensor in NC mode. Either will work, but we want to to fail open.
    - Connect sensor to GPIO pin and ground. I chose a GPIO right next to a ground for convenience
    - Code snippet of logger output.
  - Relay:
    - Describe relay, graphic, why used here to activate opener
    - The relay is simpler: just tickle a function to toggle the state pin.
    - Code snippet
- Putting it all together with MQTT and Home Assistant
  - I love homekit. All local network, highly integrated UI, Siri...
  - Home Assistant supports everything, can expose HomeKit devices.
  - install
    - home assistant OS
    - MQTT server
      - TLS
  - Use Tortoise to send/receive MQTT messages
    - Talk about confusing docs
  - Switch/sensor setup in HASS  
- Next steps
  - Add another sensor to top of garage door to detect closed state and open state
  - rpi3 with POE, ethernet (wifi is flaky)
--&gt;
</description>
        <pubDate>Thu, 25 Feb 2021 00:00:00 +0000</pubDate>
        <link>https://chrisdinger.com/2021/02/elixir-nerves-garage/</link>
        <guid isPermaLink="true">https://chrisdinger.com/2021/02/elixir-nerves-garage/</guid>
      </item>
    
      <item>
        <title>RADIUS authentication in SQL Developer on MacOS (updated)</title>
        <dc:creator></dc:creator>
        <description>&lt;p&gt;This post is an update to &lt;a href=&quot;/2017/02/sql-developer-radius-authentication-macos/&quot;&gt;this original overview&lt;/a&gt;,
which details configuring SQL Developer to perform RADIUS authentication on MacOS.
This updated post contains newer versions numbers and simpler instructions.&lt;/p&gt;

&lt;h3 id=&quot;1-install-instantclient&quot;&gt;1. Install instantclient&lt;/h3&gt;

&lt;p&gt;Download &lt;a href=&quot;http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html&quot;&gt;instantclient 12.2.0.1.0&lt;/a&gt; from Oracle.
You’ll only need the basic package. Unzip this package and place it in an accessible location.
I install my instant client in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/lib/instantclient_12_2&lt;/code&gt;. You can install yours
wherever you like.&lt;/p&gt;

&lt;p&gt;Homebrew users: the Homebrew packages available strip out the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.jar&lt;/code&gt; files which
confuses SQL Developer; download this library directly from Oracle.&lt;/p&gt;

&lt;h3 id=&quot;2-sqlnetora&quot;&gt;2. sqlnet.ora&lt;/h3&gt;

&lt;p&gt;The OCI driver properties can be configured via a special file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlnet.ora&lt;/code&gt;. The property
we’re interested in is &lt;a href=&quot;https://docs.oracle.com/cd/E11882_01/network.112/e10835/sqlnet.htm#NETRF2035&quot;&gt;SQLNET.AUTHENTICATION_SERVICES&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlnet.ora&lt;/code&gt; file and place it in a directory you’ll reference later as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TNS_ADMIN&lt;/code&gt;. I put mine alongside the
instantclient directory in step 1: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/lib/sqlnet.ora&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Make sure the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlnet.ora&lt;/code&gt; file has an entry for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;radius&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SQLNET.AUTHENTICATION_SERVICES=(none,radius)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-mangle-the-javalibrarypath-property&quot;&gt;3. Mangle the java.library.path property&lt;/h3&gt;

&lt;p&gt;Modern MacOSes—version 10.11 (El Capitan) and newer—have a feature called System Integrity Protection (SIP)
which ignores the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DYLD_LIBRARY_PATH&lt;/code&gt; environment variable. This is how the instantclient path
is typically revealed to SQL Developer. SQL Developer still looks at this environment variable even though we
can no longer use it. It’s simply not an option for setting this path.&lt;/p&gt;

&lt;p&gt;Instead we can clobber the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.library.path&lt;/code&gt; via a not-so-invasive configuration file, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.sqldeveloper/&amp;lt;VERSION&amp;gt;/product.conf&lt;/code&gt;.
Open this file and set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.library.path&lt;/code&gt; to your instantclient path (the one you chose in step 1) at the very bottom.
If you chose the same location I did, yours should look like this (replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;YOUR_USERNAME&amp;gt;&lt;/code&gt; with your username):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;AddVMOption -Djava.library.path=/Users/&amp;lt;YOUR_USERNAME&amp;gt;/lib/instantclient_12_2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-set-the-tns_admin-path-for-gui-applications&quot;&gt;4. Set the TNS_ADMIN path for GUI applications&lt;/h3&gt;

&lt;p&gt;You must set the TNS_ADMIN environment variable to make this work. Note that this needs to be set in
the GUI environment via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchctl&lt;/code&gt;—simply setting the environment variable
in a terminal will not work. Unfortunately, values set via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchctl&lt;/code&gt; don’t persist between sessions
(if you logout or restart, the value is lost).&lt;/p&gt;

&lt;p&gt;In order to set this value upon start, we’ll create the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.sqldeveloper/env.sh&lt;/code&gt;.
SQL Developer uses this file to set environment variables upon startup.&lt;/p&gt;

&lt;p&gt;Create/open this file and add a reference to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TNS_ADMIN&lt;/code&gt; location:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;launchctl setenv TNS_ADMIN /Users/&amp;lt;YOUR_USERNAME&amp;gt;/lib
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TNS_ADMIN&lt;/code&gt; should point to a directory—not a file.&lt;/p&gt;

&lt;h3 id=&quot;4-configure-sql-developer-to-use-oci&quot;&gt;4. Configure SQL Developer to use OCI&lt;/h3&gt;

&lt;p&gt;Now, fire up SQL Developer. Navigate to &lt;em&gt;Oracle SQL Developer&lt;/em&gt; &amp;gt; &lt;em&gt;Preferences&lt;/em&gt; and choose &lt;em&gt;Database&lt;/em&gt; &amp;gt; &lt;em&gt;Advanced&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/instantclient1.png&quot; alt=&quot;SQL Developer screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Check the &lt;em&gt;Use Oracle Client&lt;/em&gt; and &lt;em&gt;Use OCI/Thick driver&lt;/em&gt; checkboxes.&lt;/p&gt;

&lt;p&gt;Then, click &lt;em&gt;Configure&lt;/em&gt; and enter the location of your instantclient:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/instantclient2.png&quot; alt=&quot;SQL Developer screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Make sure you change the &lt;em&gt;Client type&lt;/em&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Instant Client&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You may click the &lt;em&gt;Test&lt;/em&gt; button here if you want, though I’ve found that its output is not always useful (failures here do not necessarily
indicate a broken configuration).&lt;/p&gt;

&lt;h3 id=&quot;5-verify&quot;&gt;5. Verify&lt;/h3&gt;

&lt;p&gt;In SQL Developer, go to &lt;em&gt;Oracle SQL Developer&lt;/em&gt; &amp;gt; &lt;em&gt;About Oracle SQL Developer&lt;/em&gt;. Click on the &lt;em&gt;Properties&lt;/em&gt; tab
and search for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqldeveloper.oci.available&lt;/code&gt; property. If the value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;, everything is set up correctly.
You should now be able to authenticate using RADIUS!&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://community.oracle.com/thread/3995451&quot;&gt;https://community.oracle.com/thread/3995451&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://ilmarkerm.blogspot.com/2015/03/setting-up-sql-developer-with-instant.html&quot;&gt;http://ilmarkerm.blogspot.com/2015/03/setting-up-sql-developer-with-instant.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 02 Jul 2018 00:00:00 +0000</pubDate>
        <link>https://chrisdinger.com/2018/07/sql-developer-radius-authentication-macos-updated/</link>
        <guid isPermaLink="true">https://chrisdinger.com/2018/07/sql-developer-radius-authentication-macos-updated/</guid>
      </item>
    
      <item>
        <title>How I learned to stop whining and tolerate my touchbar MacBook Pro</title>
        <dc:creator></dc:creator>
        <description>&lt;p&gt;I was nervous about ordering my new touchbar-enhanced MacBook Pro. As a
long-time vim user I was already sold on the value of keeping my eyes on the screen and off the keyboard. I couldn’t
be less interested in glancing down to tap a distorted rendering of a Safari tab or an OK button on a dialog. 
But mostly I was worried about losing my precious escape key.&lt;/p&gt;

&lt;p&gt;Once I got the machine and started using it, I found it to be even more awkward than I anticipated.
After a several weeks of desperately groping for a phantom escape key I’d had enough.
I knew of two physical escape key alternatives: remapping cap lock and ye olde &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crtl+[&lt;/code&gt;. The most realistic
for me was to re-purpose the caps lock key (which I often completely disable anyway). In MacOS, this is expectedly simple: navigate to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System Preferences&lt;/code&gt; &amp;gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Keyboard&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Modifier Keys&lt;/code&gt;. Just change the Caps Lock Key option to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Escape&lt;/code&gt; and you’re good to go.&lt;/p&gt;

&lt;p&gt;I had wanted to try this in the past but was very concerned about re-training my fingers. After going through with
this, I’m happy to report that it only took a day—maybe two—to become comfortable with the new configuration.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/caps_lock_as_escape.gif&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

</description>
        <pubDate>Thu, 06 Jul 2017 00:00:00 +0000</pubDate>
        <link>https://chrisdinger.com/2017/07/remap-esc-key-in-touchbar-macbook/</link>
        <guid isPermaLink="true">https://chrisdinger.com/2017/07/remap-esc-key-in-touchbar-macbook/</guid>
      </item>
    
      <item>
        <title>RADIUS authentication in SQL Developer on MacOS</title>
        <dc:creator></dc:creator>
        <description>&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;There’s an &lt;a href=&quot;/2018/07/sql-developer-radius-authentication-macos-updated&quot;&gt;updated, simpler version of this post&lt;/a&gt; that you’ll want to use for current versions of SQL Developer.&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post will walk you through configuring SQL Developer to perform RADIUS authentication on MacOS.
I periodically need to do this to connect to databases that require multi-factor authentication (MFA) and I
struggle with it every time. More than anything, this is just yet another entry in my Oracle Survival Guide to help
future-me waste less time. Hopefully it helps others too!&lt;/p&gt;

&lt;h2 id=&quot;a-note-on-versions&quot;&gt;A note on versions&lt;/h2&gt;

&lt;p&gt;Version matters here. While SQL Developer will let you point to any
instant client you like, it’ll secretly be expecting a specific version that depends
on which version of SQL Developer you’re using. If you don’t point it to that exact
version of Instant Client, things won’t work—and it won’t be clear why.&lt;/p&gt;

&lt;!--Here are the version mappings I&apos;ve had success with:--&gt;

&lt;!--| SQL Developer   | Instant Client |--&gt;
&lt;!--|-----------------|----------------|--&gt;
&lt;!--| `4.1`           | `11.2`         |--&gt;
&lt;!--| `4.2.0`         | `12.1.0.2`     |--&gt;

&lt;p&gt;As of the writing of this post, the latest versions are
&lt;a href=&quot;http://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/sqldev-ea-42-3211401.html&quot;&gt;SQL Developer 4.2.0&lt;/a&gt;
and
&lt;a href=&quot;http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html&quot;&gt;Instant Client 12.1.0.2&lt;/a&gt;. That’s what I’ll use in this guide.&lt;/p&gt;

&lt;p&gt;Download these resources directly from Oracle. The Homebrew packages available strip out the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.jar&lt;/code&gt; files which
confuses SQL Developer.&lt;/p&gt;

&lt;h3 id=&quot;1-install-instantclient&quot;&gt;1. Install instantclient&lt;/h3&gt;

&lt;p&gt;Download &lt;a href=&quot;http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html&quot;&gt;instantclient 12.1.0.2&lt;/a&gt; from Oracle.
You’ll only need the basic package. Unzip this package and place it in an accessible location.
I install my instant client in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/lib/instantclient_12_1&lt;/code&gt;. You can install yours
wherever you like.&lt;/p&gt;

&lt;p&gt;Oracle delivers this library to you in a &lt;em&gt;broken&lt;/em&gt; state. Your instantclient contains a dynamic library called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libclntsh.dylib.12.1&lt;/code&gt;,
but every bit of software you’ll want to use will be looking for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libclntsh.dylib&lt;/code&gt;.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt; into your instantclient directory and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ln -s libclntsh.dylib.12.1 libclntsh.dylib&lt;/code&gt; to &lt;em&gt;un-broken&lt;/em&gt; it.&lt;/p&gt;

&lt;h3 id=&quot;2-sqlnetora&quot;&gt;2. sqlnet.ora&lt;/h3&gt;

&lt;p&gt;The OCI driver properties can be configured via a special file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlnet.ora&lt;/code&gt;. The property
we’re interested in is &lt;a href=&quot;https://docs.oracle.com/cd/E11882_01/network.112/e10835/sqlnet.htm#NETRF2035&quot;&gt;SQLNET.AUTHENTICATION_SERVICES&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlnet.ora&lt;/code&gt; file and place it in a directory you’ll reference later as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TNS_ADMIN&lt;/code&gt;. 
Make sure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlnet.ora&lt;/code&gt; file has an entry for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;radius&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SQLNET.AUTHENTICATION_SERVICES=(none,radius)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-mangle-the-javalibrarypath-property&quot;&gt;3. Mangle the java.library.path property&lt;/h3&gt;

&lt;p&gt;Modern MacOSes—version 10.11 (El Capitan) and newer—have a feature called System Integrity Protection (SIP)
which ignores the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DYLD_LIBRARY_PATH&lt;/code&gt; environment variable. This is how the instantclient path
is typically revealed to SQL Developer. SQL Developer still looks at this environment variable even though we
can no longer use it. It’s simply not an option for setting this path.&lt;/p&gt;

&lt;p&gt;Instead we can clobber the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.library.path&lt;/code&gt; via a not-so-invasive configuration file, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.sqldeveloper/4.2.0/sqldeveloper.conf&lt;/code&gt;.
Create this file and set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.library.path&lt;/code&gt; to your instantclient path:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;AddVMOption -Djava.library.path=/Users/ding0057/lib/instantclient_12_1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-set-the-tns_admin-path-for-gui-applications&quot;&gt;4. Set the TNS_ADMIN path for GUI applications&lt;/h3&gt;

&lt;p&gt;You must set the TNS_ADMIN environment variable to make this work. Note that this needs to be set in
the GUI environment via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchctl&lt;/code&gt;—simply setting the environment variable
in a terminal will not work. Unfortunately, values set via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchctl&lt;/code&gt; don’t persist between sessions
(if you logout or restart, the value is lost).&lt;/p&gt;

&lt;p&gt;In order to set this value upon start, create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/LaunchAgents/oracle.plist&lt;/code&gt;
file that looks like this (replace the path with your own):&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;plist&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;Set TNS_ADMIN at login&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;sh&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;-c&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;launchctl setenv TNS_ADMIN /directory/where/sqlnet.ora/lives&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;RunAtLoad&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TNS_ADMIN&lt;/code&gt; should point to a directory—not a file.&lt;/p&gt;

&lt;p&gt;You’ll need to logout or restart for this change to become active.&lt;/p&gt;

&lt;h3 id=&quot;4-configure-sql-developer-to-use-oci&quot;&gt;4. Configure SQL Developer to use OCI&lt;/h3&gt;

&lt;p&gt;Now, fire up SQL Developer. Navigate to &lt;em&gt;Oracle SQL Developer&lt;/em&gt; &amp;gt; &lt;em&gt;Preferences&lt;/em&gt; and choose &lt;em&gt;Database&lt;/em&gt; &amp;gt; &lt;em&gt;Advanced&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/instantclient1.png&quot; alt=&quot;SQL Developer screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Check the &lt;em&gt;Use Oracle Client&lt;/em&gt; and &lt;em&gt;Use OCI/Thick driver&lt;/em&gt; checkboxes.&lt;/p&gt;

&lt;p&gt;Then, click &lt;em&gt;Configure&lt;/em&gt; and enter the location of your instantclient:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/instantclient2.png&quot; alt=&quot;SQL Developer screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Don’t worry about clicking the &lt;em&gt;Test&lt;/em&gt; button here. Its output is not useful (failures here do not necessarily
indicate a broken configuration). Even when everything is working this test fails—ignore it.&lt;/p&gt;

&lt;h3 id=&quot;5-verify&quot;&gt;5. Verify&lt;/h3&gt;

&lt;p&gt;In SQL Developer, go to &lt;em&gt;Oracle SQL Developer&lt;/em&gt; &amp;gt; &lt;em&gt;About Oracle SQL Developer&lt;/em&gt;. Click on the &lt;em&gt;Properties&lt;/em&gt; tab
and search for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqldeveloper.oci.available&lt;/code&gt; property. If the value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;, everything is set up correctly.
You should now be able to authenticate using RADIUS!&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://community.oracle.com/thread/3995451&quot;&gt;https://community.oracle.com/thread/3995451&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://ilmarkerm.blogspot.com/2015/03/setting-up-sql-developer-with-instant.html&quot;&gt;http://ilmarkerm.blogspot.com/2015/03/setting-up-sql-developer-with-instant.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 24 Feb 2017 00:00:00 +0000</pubDate>
        <link>https://chrisdinger.com/2017/02/sql-developer-radius-authentication-macos/</link>
        <guid isPermaLink="true">https://chrisdinger.com/2017/02/sql-developer-radius-authentication-macos/</guid>
      </item>
    
      <item>
        <title>Postgres foreign data wrapper for Oracle (oracle_fdw) on MacOS</title>
        <dc:creator></dc:creator>
        <description>&lt;p&gt;The Ecto adapter landscape currently lacks an Oracle option. If you’re itching
to try out Elixir and/or Phoenix but really need to connect to an Oracle
database to do anything interesting, you may want to give Laurenz Albe’s
&lt;a href=&quot;https://github.com/laurenz/oracle_fdw&quot;&gt;oracle_fdw&lt;/a&gt; project a try.
oracle_fdw implements a foreign data wrapper for Postgres that allows you to
connect to, read from, and manipulate remote Oracle databases.&lt;/p&gt;

&lt;p&gt;Oracle_fdw relies on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DYLD_LIBRARY_PATH&lt;/code&gt; to properly link Oracle libraries at
runtime, but MacOS version 10.11 (El Capitan) and newer ignore &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DYLD_LIBRARY_PATH&lt;/code&gt; as
part of its System Integrity Protection (SIP) feature. These steps will help you both
build and install oracle_fdw on MacOS.&lt;/p&gt;

&lt;h3 id=&quot;1-install-postgres&quot;&gt;1. Install Postgres&lt;/h3&gt;

&lt;p&gt;The easiest way to install Postgres is homebrew: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install postgres&lt;/code&gt;. Once installed
it can be started with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew services start postgres&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;2-install-instantclient&quot;&gt;2. Install instantclient&lt;/h3&gt;

&lt;p&gt;Download an &lt;a href=&quot;http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html&quot;&gt;instantclient&lt;/a&gt;
from Oracle. You’ll need both the basic package and the SDK package.
Out of the box, oracle_fdw expects that you’ll be using version 11.2. If you want
to use 12.1, you’ll need to create some symlinks to trick it.&lt;/p&gt;

&lt;p&gt;I install my instant client in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/lib/instantclient_11_2&lt;/code&gt;. You can install yours
wherever you like—just be sure that you expose an environment variable called
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ORACLE_HOME&lt;/code&gt; that points to your installation.&lt;/p&gt;

&lt;h3 id=&quot;3-build-oracle_fdw&quot;&gt;3. Build oracle_fdw&lt;/h3&gt;

&lt;p&gt;Download the &lt;a href=&quot;https://github.com/laurenz/oracle_fdw/releases&quot;&gt;latest release&lt;/a&gt; of oracle_fdw
and unpack it. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt; into the unpacked directory and compile the extension with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once oracle_fdw is compiled, you have two options for proceeding: &lt;a href=&quot;#option-1-alter-the-oraclefdw-binary&quot;&gt;modify the compiled binary&lt;/a&gt;
or &lt;a href=&quot;#option-2-symlink-libclntshdylib111-to-an-expected-location&quot;&gt;symlink your instantclient&lt;/a&gt; to a location it can find. Without doing one of these, you’ll
likely encounter this error upon loading the extension in Postgres:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ERROR:  could not load library &quot;/usr/local/lib/postgresql/oracle_fdw.so&quot;:
  dlopen(/usr/local/lib/postgresql/oracle_fdw.so, 10):
  Library not loaded: @rpath/libclntsh.dylib.11.1
  Referenced from: /usr/local/lib/postgresql/oracle_fdw.so
  Reason: image not found
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;option-1-alter-the-oracle_fdw-binary&quot;&gt;Option 1: alter the oracle_fdw binary&lt;/h4&gt;

&lt;p&gt;This is the option I chose. It lets me tweak the compiled binary while leaving everything
else as-is. After compiling with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt; (and before &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make install&lt;/code&gt;), issue this command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;install_name_tool -add_rpath $ORACLE_HOME oracle_fdw.so
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This adds your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ORACLE_HOME&lt;/code&gt; path to the list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rpath&lt;/code&gt;s in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oracle_fdw.so&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;option-2-symlink-libclntshdylib111-to-an-expected-location&quot;&gt;Option 2: symlink libclntsh.dylib.11.1 to an expected location&lt;/h4&gt;

&lt;p&gt;The other option is to symlink &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libclntsh.dylib.11.1&lt;/code&gt; to one of the default
search paths, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/lib&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ln -s $ORACLE_HOME/libclntsh.dylib.11.1 ~/lib/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-install-oracle_fdw&quot;&gt;4. Install oracle_fdw&lt;/h3&gt;

&lt;p&gt;Now that oracle_fdw is compiled and ready to link up the instantclient, install
it and its friends with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At this point you should be able to fire up your favorite Postgres client and enable the
extension with this SQL:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CREATE EXTENSION oracle_fdw;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you don’t see any gnarly errors, you’re in business! I’ll write up another post
soon that demonstrates how to use the extension.&lt;/p&gt;

</description>
        <pubDate>Tue, 20 Dec 2016 00:00:00 +0000</pubDate>
        <link>https://chrisdinger.com/2016/12/postgres_oracle_foreign_data_wrapper_macos/</link>
        <guid isPermaLink="true">https://chrisdinger.com/2016/12/postgres_oracle_foreign_data_wrapper_macos/</guid>
      </item>
    
      <item>
        <title>Using erloci to talk to Oracle in Elixir</title>
        <dc:creator></dc:creator>
        <description>&lt;p&gt;One of the great things about Elixir is its clean Erlang interoperability. The
adoption rate of new languages like Elixir depends greatly on the ecosystem of
available libraries. While the Elixir ecosystem is growing, being able to lean on the
twenty(ish) years of existing open source Erlang libraries helps a lot.&lt;/p&gt;

&lt;p&gt;Here are some examples of interacting with an Oracle database from Elixir via the
&lt;a href=&quot;https://github.com/K2InformaticsGmbH/erloci&quot;&gt;erloci&lt;/a&gt; Erlang library.&lt;/p&gt;

&lt;h3 id=&quot;connect&quot;&gt;Connect&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;erloci&lt;/code&gt; requires you to specify connection details as a TNS string. This can be
slightly annoying but you can easily interpolate host, port, and service name into
a TNS-style format.&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;c1&quot;&gt;# connect&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;somehost.yourorg.com&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;1521&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;someservice&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;username&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;password&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;tns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=tcp)(HOST=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)(PORT=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)))(CONNECT_DATA=(SERVICE_NAME=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)))&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;oci&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:erloci&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oci&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# DDL&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prep_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;create table blahs (id integer, name varchar(100))&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:executed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exec_stmt&lt;/span&gt;

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

&lt;h3 id=&quot;select&quot;&gt;Select&lt;/h3&gt;

&lt;p&gt;All SQL interactions with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;erloci&lt;/code&gt; are proper prepared statements. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec_stmt/0&lt;/code&gt; function
returns a list of column and type information. Data must be explicitly fetched with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch_rows/1&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;iex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prep_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;select * from blahs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;iex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exec_stmt&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ID&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:SQLT_NUM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;38&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;NAME&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:SQLT_CHR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;iex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fetch_rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;193&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;ahoy!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]},&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

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

&lt;p&gt;What the heck is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;2, 193, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0&amp;gt;&amp;gt;&lt;/code&gt;?
Well, from the column information returned from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec_stmt&lt;/code&gt; we know it’s of type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:SQLT_NUM&lt;/code&gt;
but what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;erloci&lt;/code&gt; is returning is binary. You’ll want to convert it using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:oci_util.oranumber_decode/1&lt;/code&gt;.
In this case, it’s the integer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;. See the &lt;a href=&quot;https://github.com/K2InformaticsGmbH/erloci/blob/master/src/oci_util.erl&quot;&gt;oci_util&lt;/a&gt;
module for a pile of other handy conversion function and utilities.&lt;/p&gt;

&lt;h3 id=&quot;insert&quot;&gt;Insert&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;erloci&lt;/code&gt; has full support for bound parameters. Parameters that are to be bound can
be named (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:id&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:name&lt;/code&gt;) or can use numbers (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:1&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:2&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:3&lt;/code&gt;) in the SQL statement.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bind_vars/1&lt;/code&gt; must then be called to supply type information for each of the bound parameters.
Finally, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec_stmt/1&lt;/code&gt; is used to pass in values and execute the insert.&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;c1&quot;&gt;# insert with bound parameters&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prep_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;insert into blahs (id, name) values (:id, :name)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bind_vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;:id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:SQLT_INT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;:name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:SQLT_CHR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}])&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:rowids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;AABUUnAQAAAEGgTAAA&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exec_stmt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;blahblah&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}])&lt;/span&gt;

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

&lt;h3 id=&quot;errors&quot;&gt;Errors&lt;/h3&gt;

&lt;p&gt;When errors are encountered &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;erloci&lt;/code&gt; returns the Oracle error code and message.&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prep_sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;create table blahs (id integer, name varchar(100))&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;955&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ORA-00955: name is already used by an existing object&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exec_stmt&lt;/span&gt;

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

</description>
        <pubDate>Wed, 23 Nov 2016 00:00:00 +0000</pubDate>
        <link>https://chrisdinger.com/2016/11/erloci-from-elixir/</link>
        <guid isPermaLink="true">https://chrisdinger.com/2016/11/erloci-from-elixir/</guid>
      </item>
    
      <item>
        <title>The Roman Numerals kata</title>
        <dc:creator></dc:creator>
        <description>&lt;p&gt;I recently read Sandi Metz’s &lt;a href=&quot;http://www.sandimetz.com/blog/2016/6/9/make-everything-the-same&quot;&gt;Make Everything the Same&lt;/a&gt;
post and her words really resonated with me. I too had recently
completed the Roman Numerals kata in Elixir and, while I was happy with my
solution, I was struck by how different it was from most others.&lt;/p&gt;

&lt;h3 id=&quot;special-cases-in-disguise&quot;&gt;Special cases in disguise&lt;/h3&gt;

&lt;p&gt;Most solutions I explored had some flavor of this kind of configuration:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;@numerals&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;M&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;900&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CM&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;D&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CD&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;90&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;XC&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;L&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;XL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;X&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;IX&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;V&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;IV&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;I&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are two things that bother me about this: the configuration contains
duplication and it makes assumptions about the implementation that uses it.&lt;/p&gt;

&lt;p&gt;The implementations that make use of this kind of configuration were indeed
short (it really only take &lt;a href=&quot;https://github.com/exercism/xelixir/blob/master/exercises/roman-numerals/example.exs&quot;&gt;three more lines of code&lt;/a&gt;) and fast, but I found
them to be opaque. When approaching the Roman Numerals kata, I tried to write
a program in the way my brain processes the conversion instead of a clever mathematical solution.
As Sandi says, code is read more than it’s written—so I prefer a solutions
that are easily understandable.&lt;/p&gt;

&lt;p&gt;To me, the only real needed configuration is this:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;@numerals&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;M&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;D&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;L&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;X&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;V&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;I&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The four and nine cases are just special combinations of these base symbols.
I’d argue that the first example contains &lt;em&gt;special cases&lt;/em&gt;—they’re just in
disguise.&lt;/p&gt;

&lt;h3 id=&quot;make-everything-the-same-functionally&quot;&gt;Make everything the same, functionally&lt;/h3&gt;

&lt;p&gt;The next step for me was to abstract the application of these symbols for any
given digit in a decimal number. A digit in a decimal number represents two things:
a numeral and a magnitude (a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt; in the ones’ place is different than a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt; in the hundreds’
place). In Roman numeral notation the symbol changes at different magnitudes, but
the pattern is always the same. This pattern is what I chose to abstract.&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;digit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;five&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ten&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;five&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;five&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;five&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;five&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;five&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ten&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Give the template
a digit and the Roman symbols for that magnitude, and it gives you the Roman numeral
representation of that digit.&lt;/p&gt;

&lt;p&gt;The magnitude here is simply the position of the Arabic numeral you’re currently
processing:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;magnitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decimal_place&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:math&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decimal_place&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;round&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then that magnitude maps nicely onto our configuration to reveal the Roman symbols
the template is after.&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;digit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@numerals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;magnitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                &lt;span class=&quot;nv&quot;&gt;@numerals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;magnitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                &lt;span class=&quot;nv&quot;&gt;@numerals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;magnitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now yes, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;template&lt;/code&gt; function contains the verboten &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;case&lt;/code&gt; statement, but its intention here
is not to branch off to other behaviors in the code. It’s simply formatting the supplied
numerals. To me at least, this &lt;em&gt;feels&lt;/em&gt; different.&lt;/p&gt;

&lt;p&gt;My most recent version is &lt;a href=&quot;https://gist.github.com/cdinger/9b6ac68c090fb2299afd1997061cfa56&quot;&gt;this solution&lt;/a&gt;
and I’m &lt;em&gt;mostly&lt;/em&gt; happy with it. Though I’m already tempted to revisit
this kata. It’d be interesting to refactor away the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;case&lt;/code&gt; statement
altogether, which might teach me a new thing or two about Elixir in the process.
I suppose that’s the point!&lt;/p&gt;
</description>
        <pubDate>Thu, 07 Jul 2016 00:00:00 +0000</pubDate>
        <link>https://chrisdinger.com/2016/07/the-roman-numerals-kata/</link>
        <guid isPermaLink="true">https://chrisdinger.com/2016/07/the-roman-numerals-kata/</guid>
      </item>
    
      <item>
        <title>Self-sourcing bash profile</title>
        <dc:creator></dc:creator>
        <description>&lt;p&gt;If you’re like me, you’re constantly popping into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.bash_profile&lt;/code&gt; (or it’s
friends) and making tweaks. I’ve always kept an alias for this, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eb&lt;/code&gt;. That
makes the edit quicker, but the obnoxious &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source ~/.bash_profile&lt;/code&gt; remains.
Of course you could plop another alias down for the sourcing, but that’s kind
of gross. Instead, you can make your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eb&lt;/code&gt; alias source after the edit is
complete:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;alias eb=&quot;vim ~/.bash_profile &amp;amp;&amp;amp; source ~/.bash_profile&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When you use that alias, you pop into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/bash_profile&lt;/code&gt;, save your change, and
continue to hack away.&lt;/p&gt;
</description>
        <pubDate>Thu, 23 Jul 2015 00:00:00 +0000</pubDate>
        <link>https://chrisdinger.com/2015/07/self-sourcing-bash-profile/</link>
        <guid isPermaLink="true">https://chrisdinger.com/2015/07/self-sourcing-bash-profile/</guid>
      </item>
    
      <item>
        <title>Using ssh bastion hosts without going mad</title>
        <dc:creator></dc:creator>
        <description>&lt;p&gt;My employer uses ssh bastion hosts. This means in order to ssh into any server,
I must first connect to a known secure server, or
‘&lt;a href=&quot;http://en.wikipedia.org/wiki/Bastion_host&quot;&gt;bastion host&lt;/a&gt;’.&lt;/p&gt;

&lt;p&gt;To deal with the double-connection pain, most people I know maintain a long
list aliases to the servers they most commonly ssh into. For years I too
maintained one of these wild configurations in my dotfiles, changing and
committing it every time we stood up new servers. No more!&lt;/p&gt;

&lt;h3 id=&quot;ssh-config&quot;&gt;SSH config&lt;/h3&gt;

&lt;p&gt;My &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ssh/config&lt;/code&gt; now contains these four precious lines:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Host bastion
  HostName some-bastion-host.umn.edu

Host *.umn.edu
  ProxyCommand ssh bastion nc %h %p
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This configuration will match any host under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;umn.edu&lt;/code&gt; and transparently proxy it
through the bastion host using &lt;a href=&quot;http://en.wikipedia.org/wiki/Netcat&quot;&gt;netcat&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;bash-completion&quot;&gt;Bash completion&lt;/h3&gt;

&lt;p&gt;I also use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash-completion&lt;/code&gt; on my Mac (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install bash-completion&lt;/code&gt;). This
allows me to auto-complete hostnames that are in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.ssh/known_hosts&lt;/code&gt;.
With bash-completion available, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh blah+TAB&lt;/code&gt; auto-completes to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh blah.long-department-name.random-number.umn.edu&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So in the past, my ssh aliases existed for two reasons: to perform the proxy,
and to shorten crazy-long hostnames. This ssh config and bash-completion make
the need to maintain aliases disappear.&lt;/p&gt;
</description>
        <pubDate>Fri, 20 Feb 2015 00:00:00 +0000</pubDate>
        <link>https://chrisdinger.com/2015/02/using-ssh-bastion-hosts-without-going-mad/</link>
        <guid isPermaLink="true">https://chrisdinger.com/2015/02/using-ssh-bastion-hosts-without-going-mad/</guid>
      </item>
    
      <item>
        <title>In search of better PeopleSoft effective date queries, Part 2</title>
        <dc:creator></dc:creator>
        <description>&lt;p&gt;In &lt;a href=&quot;/2014/10/faster-peoplesoft-effective-date-queries/&quot;&gt;my previous post&lt;/a&gt;, I made the argument that
this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emplid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acad_career&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdnt_car_nbr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effseq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effseq&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emplid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acad_career&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stdnt_car_nbr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sysdate&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emplid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acad_career&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stdnt_car_nbr&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_effdt_ps_acad_prog&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emplid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_effdt_ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emplid&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acad_career&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_effdt_ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acad_career&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdnt_car_nbr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_effdt_ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdnt_car_nbr&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_effdt_ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emplid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acad_career&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdnt_car_nbr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effdt_and_effseq_acad_prog&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emplid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effdt_and_effseq_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emplid&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acad_career&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effdt_and_effseq_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acad_career&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdnt_car_nbr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effdt_and_effseq_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdnt_car_nbr&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effdt_and_effseq_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effseq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effdt_and_effseq_acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effseq&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;is better than this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emplid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emplid&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acad_career&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acad_career&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stdnt_car_nbr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdnt_car_nbr&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sysdate&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effseq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effseq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emplid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emplid&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acad_career&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acad_career&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stdnt_car_nbr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdnt_car_nbr&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;But that first query is &lt;em&gt;way&lt;/em&gt; bigger. Other than a speed boost, it may not clear why I think
the first query is better, so I thought I’d dig into it a bit more. I’d like to
explain more about why I think this is better (not just &lt;em&gt;faster&lt;/em&gt;) and offer up
some alternative notation.&lt;/p&gt;

&lt;h3 id=&quot;readabilityreasonability&quot;&gt;Readability/Reasonability&lt;/h3&gt;

&lt;p&gt;I think the biggest reason to avoid correlated subqueries is not the performance
benefit, it’s the cognitive load that it forces upon us. A big, gnarly query
with many correlated subqueries makes it difficult to reason about.&lt;/p&gt;

&lt;p&gt;When you encounter a big query that’s doing more than you can take in at once,
what’s the first thing you do? You break it down. Start executing pieces of the
query to get an idea of the relations that are at play in your mind. Correlated
subqueries make that difficult because you cannot select a piece of the query and
run it individually. Especially in the case of PeopleSoft, these subqueries are
wired to other relations outside of the immediate scope.&lt;/p&gt;

&lt;p&gt;Effective date subqueries also often get thrown into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WHERE&lt;/code&gt; clause
alongside conditions that represent real business logic, clouding the intention
of the query. The goal should be to shove all of this accidental complexity
aside, leaving the minimal amount of code to represent the core of what the query
is trying to accomplish.&lt;/p&gt;

&lt;p&gt;The following query speaks for itself, doesn’t it? This should be our goal.
Any effective dated nonsense that this requires should take a back seat to this
distilled statement.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effective_acad_prog_tbl&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acad_prog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;123ASDF&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;destructuring-with-with&quot;&gt;Destructuring with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WITH&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Since we’re specifically talking PeopleSoft/Oracle here, we have the often
neglected &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WITH&lt;/code&gt; clause at our disposal. We can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WITH&lt;/code&gt; to destructure the
ugly peripheral bits into meaningful relation names.&lt;/p&gt;

&lt;p&gt;In Clojure and other lisps, destructuring is done with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; form:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acad-prog-eff-keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effective-acad-prog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;do-something&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effective-acad-prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Oracle’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WITH&lt;/code&gt; clause is very similar:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;k&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acad_prog_tbl_eff_keys&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;institution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acad_prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog_tbl&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sysdate&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;institution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acad_prog&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;effective_acad_prog_tbl&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog_tbl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog_tbl&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acad_prog_tbl_eff_keys&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog_tbl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;institution&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog_tbl_eff_keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;institution&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog_tbl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acad_prog&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog_tbl_eff_keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acad_prog&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog_tbl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps_acad_prog_tbl_eff_keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;effdt&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effective_acad_prog_tbl&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acad_prog&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;123ASDF&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WITH&lt;/code&gt; lets us nicely break up (and push aside) the non-core queries and clauses,
leaving the business logic to speak for itself.&lt;/p&gt;

&lt;h3 id=&quot;composability&quot;&gt;Composability&lt;/h3&gt;

&lt;p&gt;So my point here is really composability. Regardless of whether you’re using
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WITH&lt;/code&gt; clauses, building queries by composing discrete meaningfully-named
relations will help build clearer, faster, and more maintainable queries.&lt;/p&gt;
</description>
        <pubDate>Mon, 01 Dec 2014 00:00:00 +0000</pubDate>
        <link>https://chrisdinger.com/2014/12/better-peoplesoft-effective-date-queries-part-2/</link>
        <guid isPermaLink="true">https://chrisdinger.com/2014/12/better-peoplesoft-effective-date-queries-part-2/</guid>
      </item>
    
  </channel>
</rss>
